Дженерики, полиморфизм, интерфейсы: каково решение?

Я знаю, что название очень широко охватывает много !

и я надеюсь, что этот вопрос может перерасти в большую "Вики-информацию" по темам.

что я узнал-пока:

  • при использовании дженериков-поймите понятия (ковариация и контравариация).
  • не "неправильно использовать" концепция обобщения в сочетании с наследованием. Я сделал это, и это может заставить тебя задуматься. прямо в проблемы ковариации! Убедитесь, что вы" прервали " Общий в правильной точке наследования - если вы объединяете два.

(пожалуйста, поправьте меня - если вы думаете, что я ошибаюсь, отсутствует или не поняли ничего).

моя проблема была:

но к настоящему времени я потратил бесчисленные часы, пытаясь понять, как решить эту "большую головоломку", которая у меня на столе. И я получил несколько хороших ответов от некоторые из вас, так что пользователи уже - но теперь пришло время, чтобы получить что-то работает в большем масштабе.

Я рискнул в дженерики с этим: дженерики и полиморфизм работают вместе

и теперь я застрял на этом: ситуации, когда дженерики не будут работать

почему я заканчиваю с проблемами ковариации-из-за моей процедуры класса в моей иерархии.

поэтому мне интересно, являются ли интерфейсы моим следующим смелым шагом в этой "саге". Как сделать один " шаг " над проблемой ковариации. Одно дело-выяснить, что у вас действительно есть эта проблема , другое - "как ее обойти".

поэтому, если кто - нибудь из вас, хороших людей "там", имеет какое-либо мнение об этом-я все уши. В основном : Скажите мне пойти на интерфейсы (я никогда не делал один с нуля сам). Или. . брось мне кость в направлении, которое ты предлагаешь.

мой текущий исходный пул, как указано во второй ссылке-из верхний.

вот небольшой фрагмент из моего предыдущего в должности это показывает мою проблему ковариации. Дэвид любезно объяснил - почему я побежал в кусты.. Но теперь мне нужна информация о том, как ее обойти.

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,это будет путь.