具有特定类型的List上的C#扩展方法

时间:2018-04-28 21:21:19

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

我有以下2种扩展方法

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&#39;列表&lt; FlightRoute&GT;&#39;不包含&#39; ToDTO&#39;的定义和最好的扩展方法重载&#39; DataMapExtensions.ToDTO&lt; FlightRouteDTO&gt;(List&lt; BaseModel&gt;)&#39;要求接收器类型为&#39; List&lt; BaseModel&GT;&#39;

FlightRoute的类型为BaseModel。如果我将bar的类型更改为明确为List<BaseModel> ...,则问题就会消失。

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

我错过了一些明显的东西吗?

3 个答案:

答案 0 :(得分:5)

这只是预期的行为:您正在尝试将List<FlightRoute>用作List<BaseModel>,但仅仅因为FlitghtRoute继承自BaseModel而不是{39} ; t List<FlitghtRoute>继承自List<BaseModel>:它们是完全不同的类型。

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

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

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

那是因为IEnumerable<T>是具有协变类型参数的接口。通过查看reference source,您会注意到此接口使用out T声明为泛型类型参数,这表示T是协变的,并且当我们可以替换为任何继承类型使用IEnumerable<T>

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

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

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

您实际上不需要接受List,因为您没有使用值执行“特定于列表”的任何操作,并且AutoMapper会理解您传递给它的任何“集合”类型。 IEnumerable<T>就足够了,因为它与T有协变性,现在它适用于你的情况:

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