我试图添加到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>()
谁能解释这是为什么,并提出可能的解决方案?
答案 0 :(得分:2)
您可能要检查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());
B,类型系统损坏。您已将BaseEntity
传递给接受DerivedEntity
作为参数的方法。 BaseEntity
不是 DerivedEntity
,您不能这样做。
因此,默认情况下泛型类型是不变的。基本上,这意味着List<DerivedEntity>
不是List<BaseEntity>
,因为您不应将任何BaseEntity
添加到DerivedEntity
的列表中。因此,如果您的类包含一个接受T
(或TResult
,同样的逻辑)作为参数的方法,则您无法做您想做的事情。< / p>
但是,如果没有这种方法,则可以通过使用接口使类型协变:
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,在那里我对逆差的工作原理进行了解释。
答案 1 :(得分:-1)
您不能继承类型定义。即使T
和TResult
从BaseEntity继承,也并不意味着List<BaseEntity> = new List<T>
有效。否则,您可以List<object> list = new List<String>()
执行此操作。不过,您可以创建List<BaseEntity>
并用T
和TResult
对象填充它。
您可以
models.Add(typeof(T), new DataEtlModelRegistration<BaseEntity, BaseEntity>());
并将T
和TResult
对象插入DataEtlModelRegistration或
private readonly Dictionary<Type, DataEtlModelRegistration<T, TResult>> models = new Dictionary<Type, DataEtlModelRegistration<T, TResult>>();
如果对象的类型很重要,则很重要。
答案 2 :(得分:-1)
DataEtlModelRegistration也不是从DataEtlModelRegistration派生的。您不能在课堂上避免使用它。可以解决接口问题,因为它们支持协方差,但是我不确定接口是否适合您,因为您可能需要这些类中的数据和方法实现。
一个可能的选择是为DataEtlModelRegistration提供非通用基类,并带有所有必要的虚拟方法(甚至接口)。上面的字典将其作为值类型参数。