c# - 使用泛型方法将继承的泛型类添加到字典

标签 c# .net generics collections

我正在尝试添加到字典中,其中值是具有两个泛型的类。这两个泛型必须派生自一个抽象类 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 方法确保 TTResult 都派生自 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> ,因为您不应该添加任何 BaseEntityDerivedEntity 的列表秒。因此,如果您的类包含接受 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/

相关文章:

c# 递归反射和通用列表设置默认属性

C#,如何编写 RegEx.Replace 来替换 xml 元素的值?

c# - 从不同的多文件输入中分别获取文件

arrays - 如何在 Rust 中引入数组长度的类型参数?

c# - 在 UDP 服务器中接收数据包时连接重置

c# - 使用 Power BI Rest API 嵌入非组工作区报告

Java 泛型 : convert List<TypeA> to List<TypeB>

javascript - 在 ASP.NET/C# 中工作时 map 不显示

c# - 如何编辑文件夹/文件的修改日期?

DataGridView 中的 C# ComboBox 不可点击