C#中的通用方法未调用最大限制性重载

时间:2019-06-05 18:21:09

标签: c# linq generics overloading ienumerable

我重载了如下所示的通用方法:

public static T DoSomething<T>(T val) 
{
    return val;
}

public static IEnumerable<T> DoSomething<T>(IEnumerable<T> vals) 
{
    return vals.Select(x => DoSomething(x));
}

我遇到的问题是以下代码无法编译:

var myList = new List<SomeObject>();

// This will not compile
PropertyOfTypeIEnumerable_SomeObject = MyStaticClass.DoSomething(myList);

编译器抱怨无法将SomeObject转换为IEnumerable<SomeObject>。这表明编译器正在选择DoSomething的第一个版本,该版本采用通用类型T,而第二个版本则采用限制性更强的通用类型IEnumerable。

我可以通过将第二个重载方法重命名为DoSomethingList并显式调用该名称来确认是这种情况:

public static T DoSomething<T>(T val) 
{
    return val;
}

public static IEnumerable<T> DoSomethingList<T>(IEnumerable<T> vals) 
{
    return vals.Select(x => DoSomething(x));
}

// ...

var myList = new List<SomeObject>();

// This compiles ok
PropertyOfTypeIEnumerable_SomeObject = MyStaticClass.DoSomethingList(myList);

我的问题是,为什么编译器不选择限制性最强的匹配泛型实现来调用,并且有什么方法可以使这种行为而不必唯一地命名和显式调用呢?如果该示例为彼此继承的不同对象调用重载,则它将根据声明的变量类型选择限制性最强的重载。为什么它也不对泛型也这样做?

另一种选择是不重载方法,而是检查DoSomething方法内部以查看T是否可从IEnumerable <>赋值,但后来我不知道如何将其实际转换为我可以称之为的启用Linq方法,并且我不知道如何使编译器正常返回该Linq方法的结果,因为 I 将知道返回结果必须是IEnumerable,但是< em> compiler 不会。

1 个答案:

答案 0 :(得分:1)

C#中的泛型与C ++模板(1)有很多不同。如果我们在进行C ++元编程,它将调用更具限制性的IEnumerable<T>函数,这是对的。但是,在C#中,<T>函数与List<T>的匹配更好,因为它更精确。

我已经在上面测试了您的代码,并且它在.NET v4.0.30319上对我来说编译就很好,但是它返回List<T>类型,而不是Select调用返回的预期的简化IEnumerable<T>类型。表示已调用<T>函数。

如果要对DoSomething扩展类中的所有对象执行IEnumerable,则可以采用以下方法:

public static T DoSomething<T>(T val)
{
    switch (val)
    {
        case IEnumerable vals:
            foreach (object x in vals)
                DoSomething(x);
            break;
    }

    return val;
}

我将其设置为允许匹配其他特定类型,因为我猜每种不同的类型都会做不同的事情。如果不希望这样做,您始终可以只使用简单的if...is语句匹配。