Delphi 为什么TGeneric<;基地>;和TGeneric<;后代>;不兼容的类型?

Delphi 为什么TGeneric<;基地>;和TGeneric<;后代>;不兼容的类型?,delphi,generics,delphi-2010,Delphi,Generics,Delphi 2010,我已经开始在Delphi 2010中使用泛型,但在编译这段代码时遇到了一个问题: TThreadBase = class( TThread ) ... end; TThreadBaseList<T: TThreadBase> = class( TObjectList<T> ) ... end; TDataProviderThread = class( TThreadBase ) ... end; TDataCore = class( TInterfacedObjec

我已经开始在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;
但编译器不想编译它,它说(“”方括号替换为“()”):

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

虽然TDataProviderThread是TThreadBase的后代,但我不理解它

我必须通过硬排字来修复它:

MakeAllThreadsActive(TThreadBaseList<TThreadBase>(FProviders));
MakeAllThreadsActive(TThreadBaseList(FProviders));
有人知道编译器为什么会说这个错误吗?

类型

TList <TBase>
TList
不是的父类型

TList <TChild>
TList
泛型不能那样使用。

类型

TList <TBase>
TList
不是的父类型

TList <TChild>
TList

泛型不能那样使用。

TDataProviderThread是TThreadBase的后代,但
TThreadBaseList
不是
TThreadBaseList
的后代。这不是继承,它被称为协方差,虽然它在直觉上看起来像同一个东西,但它不是,它必须单独支持。目前,Delphi不支持它,但希望它在未来的版本中会得到支持

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

编辑:这里有一个可能的解决方案:将MakeAllThreadsActive变成一个通用方法,如下所示:

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

或者你可以按照Uwe Raabe的建议去做。任何一个都可以工作。

TDataProviderThread是TThreadBase的后代,但
TThreadBaseList
不是
TThreadBaseList
的后代。这不是继承,它被称为协方差,虽然它在直觉上看起来像同一个东西,但它不是,它必须单独支持。目前,Delphi不支持它,但希望它在未来的版本中会得到支持

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

编辑:这里有一个可能的解决方案:将MakeAllThreadsActive变成一个通用方法,如下所示:

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

或者你可以按照Uwe Raabe的建议去做。任何一个都可以工作。

其他人已经解释了为什么会出现此错误,请尝试将MakeAllThreadsActive作为TThreadBaseList的方法来解决此错误。其他人已经解释了为什么会出现此错误,请尝试将MakeAllThreadsActive作为TThreadBaseList的方法来解决此错误。+1很好的解释,虽然我主要从方法中知道协方差这个术语,但如果你只从对象中读取数据,那么它就是一个源,协方差是可以的。如果你写的是新的值,那么你有一个接收器,你需要反向变化。如果它既是一个源又是一个汇(我们必须假设TObjectList是,如果我们对它的任何方法都没有内在的了解),那么你就根本不可能有任何差异。向编译器灌输这个概念远远超出了“特殊技巧”的范围。我所说的特殊技巧包括向编译器添加对协方差和逆变的支持,然后将TObjectList的各个方法标记为协方差或逆变安全,这样编译器就可以验证它了。+1很好的解释,虽然我主要从方法中知道协方差这个术语,但如果你只从对象中读取数据,那么它就是一个源,协方差是可以的。如果你写的是新的值,那么你有一个接收器,你需要反向变化。如果它既是一个源又是一个汇(我们必须假设TObjectList是,如果我们对它的任何方法都没有内在的了解),那么你就根本不可能有任何差异。向编译器灌输这一概念远远超出了“特殊技巧”的范围。我所说的特殊技巧包括向编译器添加对协方差和逆变的支持,然后将TObjectList的各个方法标记为协方差或逆变安全,以便编译器能够验证它。