Дженерики, Полиморфизм, Интерфейсы: какое решение? - PullRequest
6 голосов
/ 08 февраля 2012

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

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

Что я выучил - до сих пор:

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

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

Но сейчас я трачу бесчисленные часы, пытаясь понять, как решить эту проблему«Большая загадка» у меня на столе.И я уже получил несколько хороших ответов от нескольких из вас, пользователей SO, - но теперь пришло время получить что-то, работающее в большем масштабе.

Я рискнул в Generics с этим: Generics and Polymorphism работаетвместе

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

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

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

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

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

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

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 ]

5 голосов
/ 08 февраля 2012

Вы не можете делать то, что хотите, но вы все равно не используете дженерики.Как сказал Роб Кеннеди, не имеет смысла иметь 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, но это не позволяет использовать список любым полезным способом.И это не очень универсально.

Интерфейсы не помогут вам с этой проблемой.Вы можете дать классам определенные возможности, то есть также элементы списков, через интерфейсы (другой вид полиморфизма).Но это не справится с ковариацией.

0 голосов
/ 08 февраля 2012

Я бы пошел на:

TCustomCustomerList = class(TBaseList<TBaseObject>)
end;

TCustomerList = class(TCustomCustomerList)
end;

Является ли это приемлемым в вашем дизайне - это совершенно другой вопрос.Если цель, которую вы пытаетесь достичь, состоит в том, чтобы назначить TCustomerList переменной TBaseList, это будет путь.

...