This has been asked several different ways already - but I haven't found my answer yet.
Can someone clarify a few things for me please. Using : Delphi XE2
I have quite a big BaseObject that I use for almost everything. Along with it I have a Generic list - BaseList.
Delarations go like this :
TBaseObject = class
... a lot of properties and methods ...
end;
TBaseList<T: TBaseObject> = class(TObjectList<T>)
... some properties and methods ...
end;
I have recently tried to change the TBaseList declaration from a very old TStringList using Objects[] property... to this never more versatile Generics list TObjectList.
But I run into some problems. The BaseUnit is one file ... and every time I descend my BaseObject I also make a specialized list to follow it.
So I would go and do something like :
TCustomer = class(TBaseObject)
... customer related stuff ...
end;
TCustomerList<T: TCustomer> = class(TBaseList<T>)
... customer list specific stuff ...
end;
But now I would like an object to contain a list - that can hold any object. And I thought I could do it like this
TControlObject = class(TBaseobject)
FGenList: TBaseList<TBaseObject>;
end;
Since BaseList and BaseObject is top of my hierarchy I assumed that such a List would be able to hold any list I could think of.
But I have a feeling that it is here I fail ...
a TBaseList<TBaseobject>
is somehow not comparable to TCustomerList<TCustomer>
...
Even if TCustomerList
and TCustomer
is descended from my base.
I was hoping to be able to use generics in the baselist for instaciating new objects.
ie. using T.Create
in a populate method.
Here is example of complete hierarchy:
Base Unit;
TBaseObject = class
end;
TBaseList<T:TBaseObject> = class(TObjectList<T>)
end;
CustomCustomer Unit;
TCustomCustomer = class(TBaseObject)
end;
TCustomCustomerList<T:TCustomCustomer> = class(TBaseList<T>)
end;
Customer Unit;
TCustomer = class(TCustomCustomer)
end;
TCustomerList<T:TCustomer> = class(TCustomCustomerList<T>)
end;
CustomPerson Unit;
TCustomPerson = class(TBaseObject)
end;
TCustomPersonList<T:TCustomPerson> = class(TBaseList<T>)
end;
Person Unit;
TPerson = class(TCustomPerson)
end;
TPersonList<T:TPerson> = class(TCustomPersonList<T>)
end;
Given the above hierarchy - why can't I :
var
aList : TBaseList<TBaseObject>; // used as a list parameter for methods
aPersonList : TPersonList<TPerson>;
aCustomerList : TCustomerList<TCustomer>;
begin
aPersonList := TPersonList<TPerson>.Create;
aCustomerList := TCustomerList<TCustomer>.Create;
aList := aCustomerList; <-- this FAILS !! types not equal ..
end;
Calling a procedure that handles the base class for all lists fails the same way ...
Procedure LogStuff(SomeList : TBaseList<TBaseObject>)
begin
writeln(Format( 'No. Elements in list : %d',[SomeList.Count]));
end;
Can someone punch me and tell me what I'm doing wrong here?
Delphi generics do not support covariance and contravariance so what you are attempting to do is not possible with the language's current syntax. I suggest you have a read of the following blog articles that cover the matter in more detail.
Fundamentally what you are attempting to do is this:
type
TBase = class;
TDerived = class(TBase);
TBaseList = TList<TBase>;
TDerivedList = TList<TDerived>;
var
BaseList: TBaseList;
DerivedList: TDerivedList;
...
BaseList := TDerivedList;//does not compile
The designers have not stopped you doing this out of spite. There is a good reason. Consider the following standard example:
type
TAnimal = class;
TCat = class(TAnimal);
TPenguin = class(TAnimal);
var
AnimalList: TList<TAnimal>;
CatList: TList<TCat>;
Penguin: TPenguin;
...
AnimalList := CatList;//does not compile because...
AnimalList.Add(Penguin);//...of the danger of this
Whilst it is reasonable to add a TPenguin
to a TList<TAnimal>
, the actual list that AnimalList
refers to is a TList<TCat>
and a penguin is not a cat.
And, if you want to think of it in the context of your example hierarchy, here's an illustration of code that justifies the language design.
aList := aCustomerList;//does not compile
aList.Add(aCustomPerson);
//this would add a TCustomPerson instance to a list containing
//TCustomer instances, but a TCustomPerson is not a TCustomer
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With