Object References

Today we completed object references as return values, and reference resolution. Here is some sample code expanded from before with explanations how to manage the instances in your Delphi code. In fact, its all very Delphi like.

Creating an Object

xSB := StringBuilder.Create('Hello CrossTalk!!!');

This is how a CrossTalk object is created. It is the same as creating a normal Delphi object. StringBuilder in this case is not an object that has been written for Delphi to emulate .NET’s StringBuilder class, this StringBuilder actually talks to the real .NET StringBuilder class and runs its code.

Stub Classes

The StringBuilder class is actually a stub class generated by the CrossTalk wizard. The stub classes provide the interface to your Delphi code. Stub classes are very lightweight and consist mostly of interface code and marshal calls. Very little instance data exists as the instance data is maintained by the real .NET object.

CrossTalk Wizard

The CrossTalk wizard is a program that allows you to select which .NET assemblies and classes that you need. It then generates Delphi stub classes. You can run the wizard again as many times as needed to adjust for interface changes in the your .NET code, or to access additional classes or assemblies.

We are still building the wizard, so there are no screenshots to display yet.

Accessing Objects

To access an object, just access its methods and properties as usual. Here is an example accessing the Length property.

WriteLn('Length: ' + IntToStr(xSB.Length));

.NET strings can be accessed as well. They are automatically converted to Delphi strings by CrossTalk in function results and properties.

WriteLn(xSB.ToString);

No conversion is needed, because CrossTalk generates ToString to return a Delphi string. While this method is called ToString, this is the same name in .NET and normally gets a string result from StringBuilder. Any method or property that returns a .NET string, will automatically be converted to a Delphi string by CrossTalk, not just the ToString method.

The conversion occurs both ways. In the case of the Append method, normally it expects a .NET string. But CrossTalk automatically generates the stub to accept a Delphi string.

xSB := xSB.Append('More');

Even indexed properties are supported.

WriteLn(xSB.Chars[0] + xSB.ToString(1, 4));
xSB.Chars[1] := 'a';

And the transformation to and from Delphi’s char type is automatic as well.

Construction and Destruction

.NET objects are reference counted and most do not have destructors. Delphi does not support reference counting on objects. (It does on interfaces and internally on strings). Because of this CrossTalk preserves these semantics making it more natural for a Delphi developer. So just like normal Delphi objects, you still need to destroy the objects when you are done with them.

xSB := StringBuilder.Create('Hello CrossTalk!!!'); try
    WriteLn('Length: ' + IntToStr(xSB.Length));
finally xSB.Free; end;

The Free will not actually free the instance. Instead it frees the Delphi stub and signals .NET that we are done with the .NET instance. If the object is not referenced from other .NET objects, .NET will garbage collect it. If Free is never called, the object will remain in memory.

Returned Objects

StringBuilder has an oddity that most are not aware of. Most developers simply use the following syntax.

xSB.Append('... more');

However if you read the documentation on StringBuilder’s methods you will notice that they return a StringBuilder instance. Thus the proper syntax actually is:

xSB := xSB.Append('... more');

While not documented, StringBuilder actually always returns Self, and so most developers use the first syntax. This can be verified by using Reflector or the .NET source code to see that this (C#’s slightly less egotistic version of Self) is returned.

Some methods return different instances, including instances you may not have a reference to yet in your code. CrossTalk handles this as well. Let’s examine the case of StringBuilder again.

xSB2 := xSB.Append('... more');

In this case we have two instance variables, xSB and xSB2. If the Append method returns a different instance, then xSB2 will need to point to a different instance of StringBuilder. In this case CrossTalk automatically creates a new stub and returns it as a result.

StringBuilder however never returns a different instance as the result of Append, and so we have different scenario. Methods or properties that return references to objects that we are already using. CrossTalk also detects this and returns not only a reference to the same .NET instance, but also the same Delphi stub class. So xSB1 = xSB, because CrossTalk knows they are the same object and will return the same stub instance that xSB points to for xSB1 to reference also.

This is not some magic of the Append method, it applies to all instance references returned from any .NET object. CrossTalk tracks and automatically reconciles them so multiple stub instances are not created for a single .NET instance. Each .NET instance will have exactly one and only one stub instance. This corresponds with regular Delphi code and allows you to check if two instances variables are the same instance simply by comparing them.

if xSB = xSB1 then begin
    .... they point to the exact same object...
end;