Delphi Handbooks Collection


Delphi XE Handbook


Delphi 2010 Handbook


May 5, 2011

Delphi Tech Tip 01: TCast.GetAs

A very technical Delphi tip, possibly the first of a series. How to perform an as cast only if the reference is not nil.

This is a very technical Delphi tip, possibly the first of a series... or maybe not. Over the 5 years I've had this blog, I rarely covered purely technical tips, but it might change (particularly if you tell me you like it).

As if not nil

The problems at hand is how to perform an "as" cast (a cast to a derived class) but only if the reference is not nil. in fact if you write,

obj as TButton

it will raise an exception in case the obj is not a TButton, but also in case obj is not assigned. Now if you write:

if obj is TButton then

you'll equally get the exception in case the obj is not assigned. so we need to change the code to:

if Assigned (obj) and (obj is TButton)

plus we have to do the actual cast. Question: can we write a single function to provide this behavior? 

TCast.GetAs

Well, that's the point of the tip. In fact it turns out we can use generics and write a generic function that has the type as parameter. However, we cannot use a global function (they don't support generics types) but a class function (the only function of a specific class). Here is the class definition and the method code:

Now we can call this like:

btn := TCast.GetAs<TButton> (Sender);

Generics open up a lot of new coding techniques to Delphi, this is just the tip of the iceberg. Hope you find it useful...

 





 

15 Comments

Delphi Tech Tip 01 TCast.GetAs 

I think if you do 
btn := TCast.GetAs<TButton> (Sender);

then btn can also be nil, and you must check ik btn is
not nil when you want to have the tag of btn


btn := TCast.GetAs<TButton> (Sender);
btn.tag:= 1;  //--> error when btn = nil
Comment by Christophe on May 5, 10:45

Delphi Tech Tip 01 TCast.GetAs 

"it will raise an exception in case the obj is not a 
TButton, but also in case obj is not assigned." - 
wrong. "as" can cast nil to any class.
Comment by Mihail on May 5, 10:48

Delphi Tech Tip 01 TCast.GetAs 

Thank you very sharing yet another example on how to use 
generics. I'm looking forward to using them! 
Comment by Tom Van Acker on May 5, 10:55

Delphi Tech Tip 01 TCast.GetAs 

 In fact this exact problem can be solved without
generics since ages. And also without having to check
if the result is assigned:

function TryCast(AObject: TObject; AClass: TClass; out
AResult): Boolean;
begin
  Result := AObject is AClass;
  if Result then
    TObject(AResult) := AObject as AClass
  else
    TObject(AResult) := nil;
end;

Anyway, I agree that generics are really nice and I
use them alot.
Comment by Stefan Glienke [http://delphisorcery.blogspot.com/] on May 5, 11:12

Why use generics? 

Is it worth it?
IMHO the following function will do the same
function GetAs(aClass: TClass; aObject: TObject): 
TObject;
begin
  if (aObject<>nil) and aObject.InheritsFrom(aClass) 
then
    result := aObject else
    result := nil;
end;
And if you set it as inline, your code will be much 
faster than using generics...
Comment by A.Bouchez [http://synopse.info] on May 5, 11:20

Interesting 

Like A.Bouchez points out - it may be overkill. The
majority of Delphi developers are still stuck in
D7-D2005 compilers. But I love little helper classes
like this :)

What would be cool is to create methods that, if
possible, are backwards compatible. Not all kinds of
functions can do this ofcourse, but some.

So if we combine your class with A.Bouchez version,
and perhaps relabel the method to "CastTo" - it would
be a very interesting little snippet.

I would also add interface casting. It wont really
make any difference but at least conceptually it could
make sense. Want to cast something? Use the TCast
class. Much easier for beginners to navigate.
Comment by Jon Lennart Aasenden [http://delphimax.wordpress.com] on May 5, 12:34

Delphi Tech Tip 01 TCast.GetAs 

@A.Bouchez - that's not the same, since the result 
type of the generic version is the class queried for. 
In yours, the result type is just TObject.

@Jon - 'What would be cool is to create methods that, 
if possible, are backwards compatible'. Why? The only 
reason for backwards compatibility is if you're 
actually using older versions. From the POV of new 
code, 'backwards compatibility' is just deadweight, 
causing bloat and sub-optimal design decisions.
Comment by Chris Rolliston [http://delphihaven.wordpress.com/] on May 5, 13:26

Delphi Tech Tip 01 TCast.GetAs 

The left operand of an "as" cast is always allowed to be nil. The result 
is simply nil. (If you read the "PUREPASCAL" version of System._AsCast, 
you'll get the wrong idea about the operator's behavior because it 
incorrectly uses the "is" operator. The documentation matches the 
assembler implementation.)

The GetAs function given here has *less* utility than the built-in 
operator because the compiler will catch and disallow invalid type-
casts at compile time, whereas this function can only detect them at 
run time. For example:

    val := button as TEdit;
    val := TCast.GetAs<TEdit>(button);

The first line won't compile because TButton and TEdit do not have a 
subclass relation. The second line compiles and evaluates to nil.
Comment by Rob Kennedy [http://www.cs.wisc.edu/~rkennedy/] on May 5, 14:12

Delphi Tech Tip 01 TCast.GetAs 

@Chris Rolliston:

There are other Object Pascal compilers beside Delphi.
While the mac compiler is in the pipeline, there is
still no alternative for Linux. Hence being compatible
with older versions of OP has it's uses
Comment by Jon Lennart Aasenden [http://delphimax.wordpress.com] on May 5, 14:30

Delphi Tech Tip 01 TCast.GetAs 

@Jon Lennart Aasenden:

"There are other Object Pascal compilers beside Delphi."

Let me see... there's FPC, which deliberates attempts
to mimic Delphi; there's GNU Pascal, which no one
uses, even if it's just about still alive; and there's
older versions of Delphi itself.

"While the mac compiler is in the pipeline, there is
still no alternative for Linux. Hence being compatible
with older versions of OP has it's uses"

It has its uses only so far as you actually want to
target those platforms right now. Even then, surely
the desired standard is not 'backwards compatibility'
as such, but 'what FPC implements of current Delphi'.
Comment by Chris Rolliston [http://delphihaven.wordpress.com/] on May 5, 19:05

Delphi Tech Tip 01 TCast.GetAs 

Marco, thanks very much for offering your tech tip. I,
for one, hope you do continue this as a series. If
anything, you will provide a forum for debate and
increase community participation and interest. Great
first tip! thanks
Comment by Colin Johnsun on May 6, 03:50

Delphi Tech Tip 01 TCast.GetAs 

@Mihail, @Rob Kennedy, I believe that what Marco wants 
to achieve (sorry Marco, but I found your explanation 
unclear or erroneous and had to read the code to 
understand) is a kind of "as" that would return nil 
instead of raising an exception when the types are not 
compatible. The case of nil Objects is not the problem 
and never was for "as" (as you both pointed out), 
while "is" would return False on nil, wrongly leading 
to believe that "as" would fail.

The "old fashion" (non generic) implementation would 
be:

if Sender is TButton then
  Result := TButton(Sender) // never nil
else
  Result := nil; // whether Sender not assigned or 
wrong type

or:

try
  Result := Sender as TButton
except
  on EInvalidCast do
    Result := nil;
end;
Comment by François [http://fgaillard.com] on May 6, 07:38

Delphi Tech Tip 01 TCast.GetAs 

 I think it's overkill, and using :

if Assigned (obj) and (obj is TButton)

Would be suffice.

Great tip for showing the power of Generics though

Keep the series going
Comment by Shane on May 6, 11:23

Delphi Tech Tip 01 TCast.GetAs 

I would rather raise an Exception than returning a null 
pointer. 
Comment by stanleyxu2005 [http://mmjd.com/] on May 8, 15:18

Delphi Tech Tip 01 TCast.GetAs 

Am I the only one who expects this to break in some 
future compiler update? :-)

(Grinning, running, and ducking.)

Warren
 
Comment by Warren [] on May 12, 15:40


Post Your Comment

Click here for posting your feedback to this blog.

There are currently 0 pending (unapproved) messages.