Почему 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));

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

Ответ 1

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

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

EDIT: Здесь вы можете найти решение: Сделать MakeAllThreadsActive в общий метод, например:

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

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

Ответ 2

Тип

TList <TBase>

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

TList <TChild>

Обобщения нельзя использовать таким образом.