Почему 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>
Обобщения нельзя использовать таким образом.