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

StackOverflow https://stackoverflow.com/questions/1687755

  •  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>

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top