为什么TGeneric <Base>和TGeneric <Descendant>不兼容?

Pet*_*hez 13 delphi generics delphi-2010

我已经开始在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;
Run Code Online (Sandbox Code Playgroud)

然后我有一些嵌套的过程:

procedure MakeAllThreadsActive(aThreads: TThreadBaseList<TThreadBase>);
begin
...
end;
Run Code Online (Sandbox Code Playgroud)

最后我想在TDataCore类的代码中调用这个嵌套过程:

MakeAllThreadsActive(FProviders);
Run Code Online (Sandbox Code Playgroud)

但编译器不想编译它,它说('<>'括号被'()'替换):

[DCC错误] LSCore.pas(494):E2010不兼容类型:'TThreadBaseList(TThreadBase)'和'TThreadBaseList(TDataProviderThread)'

虽然TDataProviderThread是TThreadBase的后代,但我不明白它.

我不得不通过艰难的类型转换来修复它:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders));
Run Code Online (Sandbox Code Playgroud)

有人知道为什么编译器会说这个错误吗?

Mas*_*ler 22

TDataProviderThread是TThreadBase的后代,但TThreadBaseList<TDataProviderThread>不是它的后代TThreadBaseList<TThreadBase>.这不是继承,它被称为协方差,虽然看起来像直觉上的相同,但它不是,它必须单独支持.目前,Delphi并不支持它,但希望它将来会发布.

这是协方差问题的基本原因:如果你传递给它的函数是期望一个TThreadBase对象的列表,并且你传递了一个TDataProviderThread对象的列表,那么就没有什么可以阻止它调用.Add并将其他一些TThreadBase对象粘贴到不是TDataProviderThread的列表,现在你有各种丑陋的问题.您需要编译器的特殊技巧,以确保不会发生这种情况,否则您将失去类型安全性.

编辑:这是一个可能的解决方案:将MakeAllThreadsActive转换为通用方法,如下所示:

procedure MakeAllThreadsActive<T: TThreadBase>(aThreads: TThreadBaseList<T>);
Run Code Online (Sandbox Code Playgroud)

或者你可以做Uwe Raabe建议的事情.任何一个都可以工作.

  • +1好的解释,即使我知道术语*协方差*主要来自方法. (2认同)
  • 如果你只是从对象中读取,那么它是**源**并且协方差是可以的.如果你正在写新的值,那么你有一个**汇**并且你想要逆转.如果它既是源又是接收器(我们必须假设TObjectList <T>,如果我们对其任何方法的内容没有内在的知识),那么你就不会有任何差异.用这个概念填充编译器远远超出了"特殊技巧". (2认同)

jpf*_*ius 6

类型

TList <TBase>
Run Code Online (Sandbox Code Playgroud)

不是父类型

TList <TChild>
Run Code Online (Sandbox Code Playgroud)

泛型不能以这种方式使用.