Почему TGeneric<Base> и TGeneric<Descendant> несовместимые типы?
-
18-09-2019 - |
Вопрос
Я начал использовать 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));
Кто-нибудь знает, почему компилятор выдает эту ошибку?
Решение
TDataProviderThread является потомком TThreadBase, но TThreadBaseList<TDataProviderThread>
не является потомком TThreadBaseList<TThreadBase>
.Это не наследование, это называется ковариация, и хотя интуитивно кажется, что это одно и то же, это не так, и это должно поддерживаться отдельно.На данный момент Delphi не поддерживает это, хотя, надеюсь, это произойдет в будущем выпуске.
Вот основная причина проблемы ковариации:Если функция, которой вы ее передаете, ожидает список объектов TThreadBase, а вы передаете ей список объектов TDataProviderThread, ничто не мешает ей вызвать .Добавьте и вставьте в список какой-нибудь другой объект TThreadBase, который не является TDataProviderThread , и теперь у вас возникнут всевозможные неприятные проблемы.Вам нужны специальные приемы от компилятора, чтобы убедиться, что этого не может произойти, иначе вы потеряете свою безопасность типов.
Редактировать:Вот возможное решение для вас:Преобразуйте MakeAllThreadsActive в универсальный метод, например, так:
procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>);
Или вы могли бы сделать то, что предложил Уве Раабе.Сработает любой из них.
Другие советы
Тип
TList <TBase>
не является родительским типом
TList <TChild>
Дженерики нельзя использовать таким образом.