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

C# Extension method on List with specific type

我有以下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!

编译器说

Error CS1929 'List< FlightRoute>' does not contain a definition for 'ToDTO' and the best extension method overload 'DataMapExtensions.ToDTO< FlightRouteDTO>(List< BaseModel>)' requires a receiver of type 'List< BaseModel>'

但是 FlightRouteBaseModel 类型。如果我将 bar 的类型明确更改为 List<BaseModel> ... 那么问题就消失了。

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

我是不是遗漏了什么明显的东西?

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

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

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

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

您实际上不需要接受 List,因为您没有对值执行任何操作 "list-specific",并且 AutoMapper 理解您传递给它的任何 "collection" 类型。 IEnumerable<T> 就足够了,因为它是关于 T 的协变,它现在在你的情况下工作正常:

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

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

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

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

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

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