具有约束的泛型方法的重载解决问题

Den*_*nis 5 c# generics overload-resolution

代码示例:

interface IFoo { }
class FooImpl : IFoo { }

static void Bar<T>(IEnumerable<T> value)
    where T : IFoo
{
}

static void Bar<T>(T source)
    where T : IFoo
{
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释,为什么这个方法调用:

var value = new FooImpl[0];
Bar(value);
Run Code Online (Sandbox Code Playgroud)

目标Bar<T>(T source)(因此,不编译)?

在解决重载时,编译器是否会考虑类型参数约束?

UPD.

避免与数组混淆.任何实现都会发生这种情况IEnumerable<T>,例如:

var value = new List<FooImpl>();
Run Code Online (Sandbox Code Playgroud)

UPD 2.

@ ken2k提到了协方差.但是,让我们忘掉FooImpl.这个:

var value = new List<IFoo>();
Bar(value);
Run Code Online (Sandbox Code Playgroud)

产生相同的错误.
我敢肯定,之间的隐式转换List<IFoo>IEnumerable<IFoo>存在,因为我可以很容易地写出这样的事情:

static void SomeMethod(IEnumerable<IFoo> sequence) {}
Run Code Online (Sandbox Code Playgroud)

并传入value其中:

SomeMethod(value);
Run Code Online (Sandbox Code Playgroud)

D S*_*ley 4

在解决重载时,编译器是否完全考虑类型参数约束?

不,因为通用约束不是函数签名的一部分。您可以通过添加除Bar通用约束之外相同的重载来验证这一点:

interface IBar { }

static void Bar<T>(IEnumerable<T> value)
    where T : IFoo
{
}

static void Bar<T>(T source)
    where T : IBar
{
    // fails to compile : Type ____ already defines a member called 'Bar' with the same parameter types
}
Run Code Online (Sandbox Code Playgroud)

您的代码无法编译的原因是编译器根据方法签名选择“最佳”匹配,然后尝试应用通用约束。

不这样做的一个可能原因是这个调用不明确:

{假设List<T>有一个Add<T>(IEnumerable<T> source) 方法}

List<object> junk = new List<object>();
junk.Add(1);   // OK
junk.Add("xyzzy") // OK
junk.Add(new [] {1, 2, 3, 4});  //ambiguous - do you intend to add the _array_ or the _contents_ of the array?
Run Code Online (Sandbox Code Playgroud)

明显的解决方法是对采用集合的方法使用不同的名称Bar(就像在 BCL 中使用Addand所做的那样AddRange)。