我试图将其值添加到具有两个泛型的类的Dictionary中。这两个泛型必须来自抽象类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
类派生。但是,我收到以下错误:
参数2:无法从'... DataEtlModelRegistration '转换为'... DataEtlModelRegistration '。
错误棉绒在以下代码上:
new DataEtlModelRegistration<T, TResult>()
谁能解释这是为什么,并提出可能的解决方案?
最佳答案
您可能要检查co- and contravariance。
您尚未向我们展示您的DataEtlModelRegistration<T, TResult>
类的定义,但让我们想象一下它有一个带有签名的方法:
void Accept(T t);
(任何接受
T
作为参数的方法都可以)。现在,我们还要想象
DerivedEntity
是从BaseEntity
继承的。现在,让我们将Universe更改为其中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
具有可用的默认ctor):model.Accept(new BaseEntity());
砰,类型系统坏了。您已将
BaseEntity
传递给接受DerivedEntity
作为参数的方法。 BaseEntity
不是DerivedEntity
,您不能这样做。因此,默认情况下泛型类型是不变的。基本上,这意味着
List<DerivedEntity>
不是List<BaseEntity>
,因为您不能将任何BaseEntity
添加到DerivedEntity
的列表中。因此,如果您的类包含一个接受T
(或TResult
,相同逻辑的方法)作为参数的方法,则您将无法做您想做的事情。但是,如果没有这样的方法,则可以使用接口使类型协变:
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
{
...
}
基本上,您是在告诉编译器“嘿,此接口将永远不会接受任何泛型类型的对象,只会返回它们”。如果接口
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
接口没有可以接受任何类型参数对象的方法。您还可以查看this question,我在其中解释了逆差的工作原理。