Delphi Handbooks Collection


Delphi XE Handbook


Delphi 2010 Handbook


May 9, 2011

Delphi Tech Tip 02: Retuning Derived Type

A second tech tip... unveiling why I suggested the first one. How to change the return type of a function in a derived class.

A second Delphi tech tip... unveiling why I suggested the first one. But before I get to that, thanks for the comments, the corrections, and all the feedback to the first tip. I didn't want to reply in comments, because the reply was worth a second post. In fact, this new post will describe the original reason I wrote the TCast.GetAs function presented in that tip. Some of you were right in suggesting many other general alternatives, but most of them wouldn't have worked in the specific scenario.

A Classic Problem: Overloading on Derived Types

The problem I was facing is a very classic problem of OOP. Suppose you have a base class, with a method returning on object of that class. Now suppose you have an inherited class, and you'd like the same method (or an overloaded version of the same virtual method) to have the same parameters, but return the derived class type. In coding terms, if TDerived inherits from TBase, I'd want to have the methods:

function TBase.Get (name: string): TBase;
function TDerived.Get (name: string): TDerived;

The reason is I want to be able to assigned the value returned by TDerived.Get to a reference of the TDerived class without an explicit cast. 

Returning a TRlxActiveRecord

To be even more specific, the method in question comes from the Delphi Relax framework, and specifically from the TRlxActiveRecord class. There is a Get method taking a "id" paraemter and retuning a new object, after executing a database query and copying data from the dataset to the object's fields. Adding a generic GetAs function, I'm able to ask for an object of the given type, converted to the type. You can see the actual code at line 349 of the RlxActiveRecord unit... following this link: http://code.marcocantu.com/p/delphirelax/source/tree/59/source/RlxActiveRecord.pas#L349.

As someone suggested, raising an exception could be a better idea. Again, I know there are other alternatives, but I think that in a simialr context the code makes more sense than in the more generic case I tried to present in the first tip. The fact is that rather than writing:

      emp := TEmployee.Get(4) as TEmployee;
    

I can write:

emp := TEmployee.Get(4);
    

Not a huge difference, I know, but I think it is more readable. In any case, this should make my reasons more clear.

But is it "The Solution"?

Of course, this is not "The Solution" of the problem. It is not what I was asking for (overloading virtual methods by return value), but it is the closest solution I can think of using Delphi as a language. Most strongly-type and static / compiled languages will share the same issue, but not all of them.

If you have better ideas (I know you have many ideas!), let me know. The blog is open for debate, and "tips with debate" is much better than "tips sitting along on the web".





 

5 Comments

Delphi Tech Tip 02 Retuning Derived Type 

I'd rather go the explicit way and separate the
polymorphic part from the covariant part:

TBase = class
protected
  function DoGet (const Name: String): TBase; virtual;
abstract;
public
  function Get (const Name: String): TBase;
end;

TDerived = class (TBase)
protected
  function DoGet (const Name: String): TBase; override;
public
  function Get (const Name: String): TDerived;
reintroduce;
end;

function TBase.Get (const Name: String): TBase;
begin
  Result := DoGet (Name);
end;

function TDerived.Get (const Name: String): TDerived;
begin
  Result := { get a TDerived somewhere };
end;

function TDerived.DoGet (const Name: String): TBase;
begin
  Result := Get (Name);
end;

This resembles covariant return types as closely as
possible.

FWIW, for those who use C++Builder, C++ supports
covariant return types directly.
Comment by Moritz Beutel [http://www.audacia-software.de] on May 10, 00:03

Delphi Tech Tip 02 Retuning Derived Type 

Moritz, 

that's a very clean approach and I like it. However, I'd 
rather avoid having to write code for each of the 
(dozens of) derived subclasses, and this is what the 
generic method does. I generally do not override Get (as 
this is a standard implementation) nor GetAs (as this is 
a "generic" implementation) in the specific classes.

Thanks a lot for the post and the C++ info, though.
Comment by Marco Cantu [http://www.marcocantu.com] on May 10, 06:48

Delphi Tech Tip 02 Retuning Derived Type 

Marco, your code example looks to me like "the" big
no-no of overloading, as it's more of an obfuscation
than a clarification.

Specific getters should get specific names, rather
than overload the higher level ones, otherwise the
code can break in too many ways as those Getter can
have a life of their own, classes can be added to the
hierarchy, etc. It's not like a virtual override,
you're changing the meaning of the code depending on
the type of the variables, rather than the concrete
object type.

It may look cool the first time you write it, but in
the long run, it's a maintainance disaster in waiting.

Just my 2 cents of experience with such overloads.
Comment by John [] on May 10, 07:47

Delphi Tech Tip 02 Retuning Derived Type 

I usualy write something like this:

{ interface part }
type
  TBase = class
  public
    function Get(name: string): TBase;
  end;
  TDerived = class(TBase)
  public
    function Get(name: string): TDerived;
  end;


{ implementation part }
function TBase.Get(name: string): TBase;
begin
  Result := { get result }
end;

function TDerived.Get(name: string): TDerived;
begin
  Result := TDerived(inherited Get(name));
end;
Comment by rkovac on May 10, 11:15

Delphi Tech Tip 02 Retuning Derived Type 

... or use generics

TBase = class;
TBaseClass = class of TBase;

TBase = class
public
  function Get<T=TBaseClass>(const Name: String): T;
end;

and your implementation creates a new object of type T
which can be decorated etc before passing back.

Cheers

D
Comment by David Moorhouse on May 10, 20:38


Post Your Comment

Click here for posting your feedback to this blog.

There are currently 0 pending (unapproved) messages.