Saturday, October 20, 2007

DN4DP#25: .NET vs Win32: Casting

This post continues the series of The Delphi Language Chapter teasers from Jon Shemitz’ .NET 2.0 for Delphi Programmers book.

Last time we looked at the .NET and Win32 differences for untyped var and out parameters. Here we look at casting issues.

Note that I do not get any royalties from the book and I highly recommend that you get your own copy – for instance at Amazon.

"Casting

There are also some casting differences[1] between the two platforms. In Win32, hard-casts are unsafe, because the compiler will not complain if you perform obviously illegal casts. Hard-casts is a way of telling the Win32 compiler; "Relax, I know what I'm doing. Just close your eyes and reinterpret these bits as the type I'm telling you it is". So there are no checks and no conversions going on – it is just a binary reinterpretation.

In .NET even hard-casts are safe, in the sense that the compiler and runtime will check that the cast is valid. The CLR will check that the source is compatible with the target type - if not, nil is returned instead. Conceptually, .NET hard-casts like this

  Target := TTargetClass(Source); 

work like this

  Target := TTargetClass(Source); 
if Source is TTargetClass then
Target := Source
else
Target := nil;

This means that a typical native Win32 pattern of

  if O is TMyObject then 
TMyObject(O).Foo;

has the same semantics in .NET, but a more slightly more efficient .NET-only alternative is

  MyObject := TMyObject(O); 
if Assigned(MyObject) then
MyObject.Foo;

As you know, casting between TObject and value types in .NET performs boxing and unboxing operations (see Chapter 5 for the details). In Win32 you can only cast value types of size 4 bytes or less to and from TObject – and the cast will only store the binary bits of the value type in the reference storage itself.

unit CastingDifferencesU;

interface

type
TMyObject = class
procedure Foo;
end;

procedure Test;

implementation

{ TMyObject }

procedure TMyObject.Foo;
begin
writeln('TMyObject.Foo');
end;

procedure Test;
var
O: TObject;
{$IFDEF CLR}
MyObject: TMyObject;
{$ENDIF}
begin
// Both Win32 and .NET supports is-checks, as-casts and hard-casts
O := TMyObject.Create;
if O is TMyObject then
TMyObject(O).Foo;
{$IFDEF CLR}
// In .NET a failing hard-casts returns nil -
// in Win32 the result it undefined (you typically break the type system)
MyObject := TMyObject(O);
if Assigned(MyObject) then
MyObject.Foo;
{$ENDIF}
end;

end.




[1] For more details about how casting works in Win32 and .NET see my blog posts at http://hallvards.blogspot.com/"

No comments:



Copyright © 2004-2007 by Hallvard Vassbotn