6

可能重复:
为什么 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;
}

(C# 4,在 LINQPad 中测试)

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

Foo(42, x => x); // prints "int"

但是如果我替换x => xIdentity,编译器将无法在两个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>)'

第二个重载如何成为有效的候选人?类型推断正确地确定TSourceint,所以方法的T参数Identity也必须是int,所以返回类型也必须是int......Identity可以是 aFunc<int,int>或 a Func<double,double>,但不是 a Func<int,double>

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

Foo<int>(42, Identity<int>); // The call is ambiguous...

这里怎么可能有歧义?据我所知,接受 a 的重载Func<int,double>不可能成为候选者。我想解释必须在规范中的某个地方,但我找不到相关的位......或者它可能是编译器中的一个错误,但我想这不太可能。

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

Foo(42, new Func<int, int>(Identity)); // prints "int"

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

4

2 回答 2

3

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

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

于 2011-01-05T00:18:28.960 回答
1

我认为LukeH是正确的。但是,要回答您问题的第二点:lambda 的代表将已经填充了所有类型(例如,始终是Func<int, int>ifTSource是 an int),这就是为什么在这种情况下没有歧义的原因。它不像函数签名,其中返回类型被忽略。

于 2011-01-05T00:42:28.843 回答