Почему TGeneric <Base> и TGeneric <Descendant> несовместимые типы?

Я начал использовать generics в Delphi 2010, но у меня возникла проблема при компиляции этого фрагмента кода:

TThreadBase = class( TThread )
...
end;

TThreadBaseList<T: TThreadBase> = class( TObjectList<T> )
...
end;

TDataProviderThread = class( TThreadBase )
...
end;

TDataCore = class( TInterfacedObject, IDataCore )
private
  FProviders: TThreadBaseList<TDataProviderThread>;
...
end;

Затем у меня есть некоторая вложенная процедура:

procedure MakeAllThreadsActive(aThreads: TThreadBaseList<TThreadBase>);
begin
...
end;

И, наконец, я хочу вызвать эту вложенную процедуру в код класса TDataCore:

MakeAllThreadsActive(FProviders);

Но компилятор не хочет компилировать его, и он говорит (скобки '<>' заменены на '()'):

[Ошибка DCC] LSCore.pas (494): E2010 Несовместимые типы: 'TThreadBaseList (TThreadBase)' и 'TThreadBaseList (TDataProviderThread)'

Я не понимаю, хотя TDataProviderThread является потомком TThreadBase.

Я должен был исправить это жестким приведением типов:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders));

Кто-нибудь знает, почему компилятор говорит об этой ошибке?

delphi,generics,delphi-2010,

12

Ответов: 2


22 ование принято

TDataProviderThread является потомком TThreadBase, но TThreadBaseList<TDataProviderThread>не является потомком TThreadBaseList<TThreadBase>. Это не наследование, это называется ковариацией , и хотя это кажется интуитивно похожим на одно и то же, это не так, и его нужно поддерживать отдельно. На данный момент Delphi его не поддерживает, хотя, надеюсь, это будет в будущем выпуске.

Вот основная причина проблемы ковариации: если функция, с которой вы передаете ее, ожидает список объектов TThreadBase, и вы передаете ей список объектов TDataProviderThread, нет ничего, что могло бы заставить его не вызывать .Add и привязать какой-то другой объект TThreadBase к список, который не является TDataProviderThread, и теперь у вас есть всевозможные уродливые проблемы. Вам нужны специальные трюки от компилятора, чтобы убедиться, что этого не произойдет, иначе вы потеряете безопасность своего типа.

EDIT: Вот вам возможное решение: Сделайте MakeAllThreadsActive в общий метод, например:

procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>);

Или вы могли бы сделать то, что предложил Уве Раабе. Любой из них будет работать.


6

Тип

TList <TBase>

не является родительским типом

TList <TChild>

Дженерики не могут быть использованы таким образом.

Дельфы, дженерики, Дельфы-2010,
Похожие вопросы