泛型,重载解析和代理(对不起,找不到更好的标题)

Tho*_*que 6 c# compiler-construction generics overload-resolution

可能重复:
为什么Func <T>与Func <IEnumerable <T >>不明确?

我注意到泛型的一个非常奇怪的重载解决问题......

请考虑以下方法:

static void Foo<TSource>(TSource element, Func<TSource, int> selector)
{
    "int".Dump();
}

static void Foo<TSource>(TSource element, Func<TSource, double> selector)
{
    "double".Dump();
}

static T Identity<T>(T value)
{
    return value;
}
Run Code Online (Sandbox Code Playgroud)

(C#4,在LINQPad中测试)

如果我尝试Foo使用lambda表达式作为选择器调用,一切正常:

Foo(42, x => x); // prints "int"
Run Code Online (Sandbox Code Playgroud)

但是,如果我替换x => xIdentity,编译器无法在2个Foo重载之间做出决定:

Foo(42, Identity);
// The call is ambiguous between the following methods or properties:
// 'UserQuery.Foo<int>(int, System.Func<int,int>)' and
// 'UserQuery.Foo<int>(int, System.Func<int,double>)'
Run Code Online (Sandbox Code Playgroud)

如何将第二次重载作为有效候选者?类型推断正确判断TSourceint,这样T的参数Identity方法是int一样,所以返回类型必须是int太...... Identity可能是一个Func<int,int>或一个Func<double,double>,但不是Func<int,double>!

它变得更糟!即使我明确指定所有类型参数,我仍然会得到相同的错误:

Foo<int>(42, Identity<int>); // The call is ambiguous...
Run Code Online (Sandbox Code Playgroud)

这里怎么会有歧义?据我所知,没有办法超载Func<int,double>可以成为一个候选人.我想解释必须在规范中的某处,但我找不到相关的位...或者它可能是编译器中的错误,但我想这不太可能.

请注意,如果我明确创建委托,它确实有效:

Foo(42, new Func<int, int>(Identity)); // prints "int"
Run Code Online (Sandbox Code Playgroud)

那么,有人可以解释这里发生了什么吗?另外,为什么它适用于lambda但不适用于方法组?

Luk*_*keH 3

这难道不是因为返回类型不是方法签名的一部分吗?

Identity<T>在尝试决定需要哪个重载时,编译器不会考虑方法的参数类型和返回类型保证相同的事实Foo<TSource>。如果不考虑返回类型,则Identity<int>同样可以转换为Func<int, int>,Func<int, double>Func<int, anything>