我正在尝试添加到字典中,其中值是具有两个泛型的类。这两个泛型必须派生自一个抽象类 BaseEntity
:
internal abstract class BaseEntity
{
...
}
internal class DataEtlModelRegistration<T, TResult> where T : BaseEntity where TResult : BaseEntity
{
...
}
internal class DataEtlContext : IDataEtlContext
{
private readonly Dictionary<Type, DataEtlModelRegistration<BaseEntity, BaseEntity>> models = new Dictionary<Type, DataEtlModelRegistration<BaseEntity, BaseEntity>>();
public void RegisterModelType<T, TResult>() where T : BaseEntity where TResult : BaseEntity
{
models.Add(typeof(T), new DataEtlModelRegistration<T, TResult>());
}
}
我希望这是有效的,因为 RegisterModelType
方法确保 T
和 TResult
都派生自 BaseEntity
根据类型约束的性质分类。
但是,我收到以下错误:
Argument 2: cannot convert from '...DataEtlModelRegistration<T, TResult>' to '...DataEtlModelRegistration<BaseEntity, BaseEntity>'.
错误 lint 在以下代码上:
new DataEtlModelRegistration<T, TResult>()
谁能解释这是为什么,并提出可能的解决方案?
最佳答案
您可能想查看 co- and contravariance .
您还没有向我们展示您的 DataEtlModelRegistration<T, TResult>
的定义类,但让我们想象它有一个带有签名的方法:
void Accept(T t);
(任何接受 T
作为参数的方法都可以)。
现在让我们也想象一下DerivedEntity
继承自 BaseEntity
.现在让我们把我们的宇宙变成一个models.Add(typeof(T), new DataEtlModelRegistration<T, TResult>());
是有效代码并调用 RegisterModelType<DerivedEntity, TAnything>
, 其中TAnything
可以是任何派生自 BaseEntity
的东西.
所以类型为 DataEtlModelRegistration<DerivedEntity, TAnything>
的对象现在在关键字 typeof(DerivedEntity)
下的字典中.让我们尝试提取它:
DataEtlModelRegistration<BaseEntity, BaseEntity> model = models[typeof(DerivedEntity)];
现在,自 entity
类型为 DataEtlModelRegistration<BaseEntity, BaseEntity>
,这段代码应该可以工作(前提是 BaseEntity
有一个可用的默认构造函数):
model.Accept(new BaseEntity());
砰,类型系统坏了。你已经通过了 BaseEntity
到接受 DerivedEntity
的方法作为参数。 BaseEntity
不是 DerivedEntity
,你不能那样做。
因此,泛型类型在默认情况下是不变的。基本上这意味着,例如List<DerivedEntity>
不是 List<BaseEntity>
,因为您不应该添加任何 BaseEntity
到 DerivedEntity
的列表秒。因此,如果您的类包含接受 T
的方法(或 TResult
,适用相同的逻辑)作为参数,您不能做您想做的事。
但是,如果没有这样的方法,那么您可以使用接口(interface)使您的类型协变:
interface IModelRegistration<out T, out TResult> where T : BaseEntity where TResult : BaseEntity
{
...
}
internal class DataEtlModelRegistration<T, TResult> : IModelRegistration<T, TResult> where T : BaseEntity where TResult : BaseEntity
{
...
}
基本上你是在告诉编译器“嘿,这个接口(interface)永远不会接受任何泛型类型的对象,它只返回它们”。如果接口(interface) IModelRegistration
,该代码将无法编译包含一个带有 T
的方法或 TResult
作为它的参数。现在可以合法地说:
private readonly Dictionary<Type, IModelRegistration<BaseEntity, BaseEntity>> models = new Dictionary<Type, IModelRegistration<BaseEntity, BaseEntity>>();
models.Add(typeof(DerivedEntity), new DataEtlModelRegistration<DerivedEntity, DerivedEntity>());
您将能够从字典中提取一个对象作为 IModelRegistration
的实例。界面。
IModelRegistration<BaseEntity, BaseEntity> model = models[typeof(DerivedEntity)];
现在您无法破坏类型系统,因为我们知道一个事实 IModelRegistration
接口(interface)没有任何方法可以接受其任何类型参数的对象。
您还可以查看 this question ,我在其中解释了逆变的工作原理。
关于c# - 使用泛型方法将继承的泛型类添加到字典,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57005118/