Заместващ знак за произволен общ списък

Имам клас MyClass, който не е общ и съдържа и прави нещо с произволен TList. Искам да заменя TList с общия TList, но MyClass трябва да остане негенеричен. Тъй като Delphi е инвариантен, нещо подобно не работи:

list1: TList<TObject>
list2: TList<MyType> //MyType extends TObject
[...]
list1 := list2

Освен това изглежда няма негенеричен суперклас на TList, а само IEnumerable.

Има ли начин да декларирате заместител/заместващ знак за TList с произволен T?


person Kiwi    schedule 14.03.2014    source източник


Отговори (2)


Това е проблем с липсата на поддръжка за Co- и Contravariance в Delphi. Въпреки това, ако знаете, че просто извършвате операции, които са ковариантни (т.е. повторение на елементите в списъка), можете просто да прехвърлите TList<MyType> към TList<TObject> като се има предвид, че MyType наследява от TObject.

Като вземем примера от статията в Wikipedia, можете да управлявате списък с котки като списък с животни, когато просто преглеждате и четете имената им или нещо подобно.

Но трябва да внимавате с действия, които се нуждаят от контравариантност (т.е. добавяне на елементи).

Отново примерът, че не можете да поставите куче в списъка с животни, защото всъщност списъкът е списък с котки.

person Stefan Glienke    schedule 14.03.2014
comment
Тези твърди отливки обикновено са лоша идея. Наивен читател на отговора може да заключи, че одобрявате такива отливки като добра идея. - person David Heffernan; 14.03.2014
comment
Вероятно най-доброто решение за сега, въпреки че кастингът не ми харесва. - person Kiwi; 14.03.2014
comment
Защо смятате, че това е най-доброто решение? Съмнявам се, че е така. Не бих предложил решение без да знам какъв е проблемът. - person David Heffernan; 14.03.2014
comment
Hardcast е легитимен. По-сложният въпрос е дали правите само ковариантни неща със list1 след (което не можете да гарантирате). Това е нещо, което .NET решава с IReadOnlyList‹T›, който е ковариантен (докато IList‹T› не е), защото не позволява манипулиране на списъка. Spring4D има тези. - person Stefan Glienke; 14.03.2014
comment
Проблемът е възникнал в съществуващ проект, който е разработен през последните ~18 години. MyClass всъщност е заместен VCL StringGrid с около 10k допълнителни реда код и се захранва със списъци, разширяващи TList, всеки със собствена цел и методи. Имах задача да променя TList на общ списък - person Kiwi; 14.03.2014

Отговорът е не, това не може да се направи. Не можете да включите в негенеричен клас обект от неинстанциран общ тип.

person David Heffernan    schedule 14.03.2014
comment
Би ли било възможно да се разшири TList‹T›, да се включи интерфейс IMyInterface и да се декларира обектът в MyClass като от тип IMyInterface? - person Kiwi; 14.03.2014
comment
Но вашето TList<TMyClass> няма да бъде TList<IMyInterface>. - person David Heffernan; 14.03.2014
comment
Мислех си за нещо подобно: MyList‹T› = class(TList‹T›) където MyList също имплементира IMyInterface и по-късно ще използвам list1: IMyInterface; списък2: MyList‹TObject› list1 := списък 2 - person Kiwi; 14.03.2014
comment
Но тогава MyList<T> е общо. - person David Heffernan; 15.03.2014