Thursday, April 2, 2009

Delphi Generics: constructor restriction

I will now try to create a new generic class that accepts a type parameter that has a “constructor” restriction (must have a default public parameterless constructor).

Let’s start off with a simple example:



type
TMyClass = class
end;

TMyGenericClass<T: constructor> = class
end;

var
GenObj: TMyGenericClass<TMyClass>;

begin
end.

This is a legitimate example that will compile. TMyClass inherits the default parameterless constructor from TObject. Even though this is not the result that you would expect in other languages, it’s still somehow correct.


Moving further, let’s check what happens if we declare a private default constructor:



type
TMyClass = class
private
constructor Create();
end;

TMyGenericClass<T: constructor> = class
end;

{ TMyClass }
constructor TMyClass.Create();
begin
WriteLn('TMyClass.Create()');
end;

var
GenObj: TMyGenericClass<TMyClass>;

begin
end.

And here we will get a compile error. The requirement that TMyClass has a public parameterless constructor is not satisfied!


But don’t be too happy just yet! The next example cracks it again:



type
TMyClass = class
public
constructor Create(const AParam: String);
end;

TMyGenericClass<T: constructor> = class
end;

{ TMyClass }
constructor TMyClass.Create(const AParam: String);
begin
WriteLn('TMyClass.Create()');
end;

var
GenObj: TMyGenericClass<TMyClass>;

begin
end.

It seems that the default constructor is yet again found — the one inherited from TObject.


The last one (I promise!) follows:



type
TMyClass = class
private
constructor Create(); overload;
public
constructor Create(const AParam: String); overload;
end;

TMyGenericClass<T: constructor> = class
end;

{ TMyClass }
constructor TMyClass.Create(const AParam: String);
begin
WriteLn('TMyClass.Create()');
end;

constructor TMyClass.Create;
begin
end;

var
GenObj: TMyGenericClass<TMyClass>;

begin
end.

Again, a compilation error is risen. We’re back on the right track.





The final conclusion to this post and the previous one is:


If you do not have/need a default public parameterless constructor in your class, you must still define a private one that would raise an Exception telling the programmer to not call it. Doing this you will ensure that your constructor is being executed and not the TObject provided one, resulting in a predictable object state. Also this will ensure that the “constructor” restriction will be enforced correctly!

No comments: