DELPHI: дженерики и полиморфизм
это уже было задано несколькими разными способами , но я еще не нашел ответа.
может кто-нибудь прояснить несколько вещей для меня, пожалуйста. Использование: Delphi XE2
у меня довольно большой BaseObject, который я использую почти для всего. Вместе с ним у меня есть общий список - BaseList.
Delarations идут так:
TBaseObject = class
... a lot of properties and methods ...
end;
TBaseList<T: TBaseObject> = class(TObjectList<T>)
... some properties and methods ...
end;
недавно я попытался изменить объявление TBaseList из очень старого TStringList, используя объекты[] свойство... к этому никогда не более универсальный список дженериков TObjectList.
но я столкнулся с некоторыми проблемами. BaseUnit-это один файл ... и каждый раз, когда я спускаюсь с моего BaseObject, я также делаю специализированный список, чтобы следовать за ним.
поэтому я бы пошел и сделал что-то вроде :
TCustomer = class(TBaseObject)
... customer related stuff ...
end;
TCustomerList<T: TCustomer> = class(TBaseList<T>)
... customer list specific stuff ...
end;
но теперь я хотел бы объект содержит список, который может содержать любой объект. И я думал, что смогу сделать это вот так!--12-->
TControlObject = class(TBaseobject)
FGenList: TBaseList<TBaseObject>;
end;
поскольку BaseList и BaseObject являются верхними из моей иерархии я предположил, что такой список сможет содержать любой список, который я мог бы придумать.
но у меня такое чувство, что именно здесь я терплю неудачу ...
а TBaseList<TBaseobject>
как-то не сопоставимо с TCustomerList<TCustomer>
...
Даже если TCustomerList
и TCustomer
происходит от моей базы.
Я надеялся быть в состоянии использовать дженерики в baselist для instaciating новые объекты.
то есть. используя T.Create
в populate метод.
вот пример полного иерархия:
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;
учитывая вышеуказанную иерархию-почему я не могу:
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;
вызов процедуры, которая обрабатывает базовый класс для всех списков, завершается одинаково ...
Procedure LogStuff(SomeList : TBaseList<TBaseObject>)
begin
writeln(Format( 'No. Elements in list : %d',[SomeList.Count]));
end;
может кто-то ударить меня и сказать мне, что я делаю неправильно здесь?
1 ответов
Делфи генерики не поддерживают ковариантность и контравариантность, так что вы пытаетесь сделать, это не возможно с текущим синтаксисом языка. Я предлагаю вам прочитать следующие статьи в блоге, которые охватывают более подробно.
- Craig Stuntz: сравнение C#, C++ и Delphi (Win32) дженерики
- Мейсон Уилер: дженерики и проблема ковариации
принципиально то, что вы попытка сделать это:
type
TBase = class;
TDerived = class(TBase);
TBaseList = TList<TBase>;
TDerivedList = TList<TDerived>;
var
BaseList: TBaseList;
DerivedList: TDerivedList;
...
BaseList := TDerivedList;//does not compile
дизайнеры не остановили вас делать это назло. На то есть веская причина. Рассмотрим следующий стандартный пример:
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
в то время как разумно добавить TPenguin
до TList<TAnimal>
, фактический список, что AnimalList
относится к TList<TCat>
а пингвин-это не кошка.
и, если вы хотите подумать об этом в контексте вашей иерархии примеров, вот иллюстрация кода, которая оправдывает язык дизайн.
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