【问题标题】:C# Extension method on List with specific type具有特定类型的列表上的 C# 扩展方法
【发布时间】:2018-10-09 09:47:11
【问题描述】:

我有以下两种扩展方法

namespace Services.Resources.Extensions
{
    public static class DataMapExtensions
    {
        public static T ToDTO<T>(this BaseModel model)
        {
            return Mapper.Map<T>(model);
        }

        public static List<T> ToDTO<T>(this List<BaseModel> models)
        {
            return Mapper.Map<List<T>>(models);
        }

    }
}

第一种方法效果很好。

//Note: FlightRoute inherits BaseModel
FlightRouteDTO foo = new FlightRoute().ToDTO<FlightRouteDTO>(); //This works!

但是,第二种方法似乎不起作用。

List<FlightRouteDTO> bar = new List<FlightRoute>().ToDTO<FlightRouteDTO>(); //This doesn't work!

编译器在说

错误 CS1929“List”不包含“ToDTO”的定义,并且最佳扩展方法重载“DataMapExtensions.ToDTO(List)”需要“List 类型的接收器” '

但是FlightRoute 的类型是BaseModel。如果我将bar 的类型明确更改为List&lt;BaseModel&gt; ...,那么问题就消失了。

List<FlightRouteDTO> bar = new List<BaseModel>().ToDTO<FlightRouteDTO>(); //Why does it only work this way?

我是否遗漏了一些明显的东西?

【问题讨论】:

  • 即使 FlightRoute 继承自 BaseModel,也不能直接调用带有 List 实例的 List 的扩展方法。
  • 我想我不明白为什么不这样做。如果我将参数更改为泛型 List 并说类型 V 继承自 BaseModel,那么就可以了。但我认为 List 也应该工作,因为继承仍然存在
  • 你应该阅读协方差 - 它会解释为什么你不能将 List&lt;TDerived&gt; 分配给 List&lt;TBase&gt;

标签: c# generics inheritance type-conversion extension-methods


【解决方案1】:

这只是预期的行为:您正在尝试将List&lt;FlightRoute&gt; 用作List&lt;BaseModel&gt;,但仅仅因为FlitghtRoute 继承自BaseModel 并不会使List&lt;FlitghtRoute&gt; 继承自List&lt;BaseModel&gt;:它们是完全不同的类型。

相反,您可以做的是利用Covariance,使用接口而不是具体类型。

通过将方法签名更改为以下内容,您会注意到不会产生编译器错误:

public static List<T> ToDTO<T>(this IEnumerable<BaseModel> models)
{
    return Mapper.Map<List<T>>(models);
}

那是因为IEnumerable&lt;T&gt; 是一个带有协变 类型参数的接口。通过查看reference source,您会注意到该接口以out T 作为泛型类型参数声明,这表明T 是协变的,并且在我们使用IEnumerable&lt;T&gt; 时可以被任何继承类型替换。

【讨论】:

  • 哇,我真的很惊讶这能奏效。我认为这种方法比添加第二个泛型类型要好。
  • 请注意,在这种情况下最好返回List&lt;T&gt;,因为无论如何automapper都会返回List&lt;T&gt;
  • @Evk 同意。我不记得自动映射器在幕后创建了哪种类型。在这种情况下最好返回更具体的类型。
【解决方案2】:

您可以引入带有约束的第二个类型参数:

public static List<T> ToDTO<T, K>(this List<K> models) where K : BaseModel
{
    return Mapper.Map<List<T>>(models);
}

【讨论】:

  • 这就是我最终要做的。只是不知道为什么我不能有 List 因为类型继承约束应该仍然存在于派生类型中
【解决方案3】:

解决此问题的更好方法可能是像这样更改签名:

public static List<T> ToDTO<T>(this IEnumerable<BaseModel> models)
{
    return Mapper.Map<List<T>>(models);
}

您实际上不需要接受List,因为您没有对值执行任何“特定于列表”的操作,并且 AutoMapper 可以理解您传递给它的任何“集合”类型。 IEnumerable&lt;T&gt; 就足够了,因为它与 T 是协变的,所以它现在在你的情况下可以正常工作:

// works
List<FlightRouteDTO> bar = new List<FlightRoute>().ToDTO<FlightRouteDTO>();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 2021-09-20
    • 2020-04-15
    • 1970-01-01
    • 2014-06-11
    • 1970-01-01
    • 2016-08-11
    相关资源
    最近更新 更多