Дженерики, полиморфизм, интерфейсы: каково решение?
Я знаю, что название очень широко охватывает много !
и я надеюсь, что этот вопрос может перерасти в большую "Вики-информацию" по темам.
что я узнал-пока:
- при использовании дженериков-поймите понятия (ковариация и контравариация).
- не "неправильно использовать" концепция обобщения в сочетании с наследованием. Я сделал это, и это может заставить тебя задуматься. прямо в проблемы ковариации! Убедитесь, что вы" прервали " Общий в правильной точке наследования - если вы объединяете два.
(пожалуйста, поправьте меня - если вы думаете, что я ошибаюсь, отсутствует или не поняли ничего).
моя проблема была:
но к настоящему времени я потратил бесчисленные часы, пытаясь понять, как решить эту "большую головоломку", которая у меня на столе. И я получил несколько хороших ответов от некоторые из вас, так что пользователи уже - но теперь пришло время, чтобы получить что-то работает в большем масштабе.
Я рискнул в дженерики с этим: дженерики и полиморфизм работают вместе
и теперь я застрял на этом: ситуации, когда дженерики не будут работать
почему я заканчиваю с проблемами ковариации-из-за моей процедуры класса в моей иерархии.
поэтому мне интересно, являются ли интерфейсы моим следующим смелым шагом в этой "саге". Как сделать один " шаг " над проблемой ковариации. Одно дело-выяснить, что у вас действительно есть эта проблема , другое - "как ее обойти".
поэтому, если кто - нибудь из вас, хороших людей "там", имеет какое-либо мнение об этом-я все уши. В основном : Скажите мне пойти на интерфейсы (я никогда не делал один с нуля сам). Или. . брось мне кость в направлении, которое ты предлагаешь.
мой текущий исходный пул, как указано во второй ссылке-из верхний.
вот небольшой фрагмент из моего предыдущего в должности это показывает мою проблему ковариации. Дэвид любезно объяснил - почему я побежал в кусты.. Но теперь мне нужна информация о том, как ее обойти.
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;
в отношении
2 ответов
вы не можете делать то, что хотите, но это не так, как вы используете дженерики в любом случае. Как сказал Роб Кеннеди, нет смысла иметь TCustomerList<TCustomer>
и TPersonList<TPerson>
. Красота дженериков в том, что вы можете использовать один и тот же список для разных типов элементов. это означает, что список и тип элемента не должны иметь никаких зависимостей.
вы можете сделать что-то вроде:
procedure TSomething.ProcessList<T: TBaseObject>(const aList: TBaseList<T>);
begin
// process the list using code that is independent of the actual type of T.
end;
...
var
aCustomerList: TBaseList<TCustomer>;
aPersonList: TBaseList<TPerson>;
begin
ProcessList(aCustomerList);
ProcessList(aPersonList);
Возможно, вам придется указать T
(некоторые ранние версии дженериков не обрабатывались тип вывода - то есть, что он выводит тип T
из типа параметра-очень хорошо), т. е.
ProcessList<TCustomer>(aCustomerList);
ProcessList<TPerson>(aPersonList);
но это, или что-то подобное, то, что вы должны сделать. Все остальное не имеет смысла, ИМО. Нет необходимости иметь переменную, которая могла бы содержать любой из этих списков, например ваш aList
. И если вам действительно нужен один, вы можете использовать только TObject
, но это не позволяет вам использовать список каким-либо полезным образом. И оно не очень общее.
интерфейсы не поможет тебе с этой проблемой. Вы можете дать классам определенные возможности, т. е. также элементы списков, через интерфейсы (другой вид полиморфизма). Но ковариация тут не поможет.
Я бы пошел :
TCustomCustomerList = class(TBaseList<TBaseObject>)
end;
TCustomerList = class(TCustomCustomerList)
end;
является ли это приемлемым в вашем дизайне, это совершенно другой вопрос. Если цель, которую вы пытаетесь достичь, - назначить TCustomerList переменной TBaseList,это будет путь.