我想知道为什么当C#3.0编译器可以隐式地为同一方法创建委托时,它作为参数传递给泛型函数时,无法推断出方法的类型。
以下是一个例子:
class Test
{
static void foo(int x) { }
static void bar<T>(Action<T> f) { }
static void test()
{
Action<int> f = foo; // I can do this
bar(f); // and then do this
bar(foo); // but this does not work
}
}
我原本以为我能够将foo
传递给bar
并让编译器从传递的函数的签名中推断出Action<T>
的类型,但这不是工作。但是我可以在没有强制转换的情况下从Action<int>
创建foo
,那么编译器是否也不能通过类型推断做同样的事情呢?
答案 0 :(得分:16)
也许这会让它更清晰:
public class SomeClass
{
static void foo(int x) { }
static void foo(string s) { }
static void bar<T>(Action<T> f){}
static void barz(Action<int> f) { }
static void test()
{
Action<int> f = foo;
bar(f);
barz(foo);
bar(foo);
//these help the compiler to know which types to use
bar<int>(foo);
bar( (int i) => foo(i));
}
}
foo不是动作 - foo是一个方法组。
编辑:我添加了两种(更多)方法来帮助编译器找出类型(即 - 如何跳过推理步骤)。
从我对JSkeet答案中的文章的阅读中,不推断类型的决定似乎是基于相互推断的场景,例如
static void foo<T>(T x) { }
static void bar<T>(Action<T> f) { }
static void test()
{
bar(foo); //wut's T?
}
由于一般问题不可解决,他们选择将解决方案存在的具体问题留下未解决的问题。
作为此决定的结果,您不会为方法添加重载,并且会从用于单个成员方法组的所有调用方中获得大量类型混淆。我想这是件好事。
答案 1 :(得分:7)
理由是,如果类型扩大,那么就不应该失败。即,如果方法foo(string)被添加到类型中,它应该对现有代码无关紧要 - 只要现有方法的内容不会改变。
因此,即使只有一个方法foo,也不能将对foo(称为方法组)的引用强制转换为非类型特定的委托,例如Action<T>
,但仅限于特定于类型的委托,例如Action<int>
。
答案 2 :(得分:5)
这有点奇怪,是的。类型推断的C#3.0规范很难阅读并且有错误,但它看起来它应该工作。在第一阶段(第7.4.2.1节)中,我认为存在错误 - 它不应该在第一个项目符号中提及方法组(因为它们没有被显式参数类型推断(7.4.2.7)覆盖 - 这意味着它应该使用输出类型推断(7.4.2.6)。看起来就像它应该有效 - 但显然它没有:(
我知道MS正在寻求改进类型推断的规范,因此它可能会变得更加清晰。我也知道,无论阅读的难度如何,对方法组和类型推断都有限制 - 当方法组实际上只是一种方法时,这种限制可能是特殊的。无可否认。
Eric Lippert在return type inference not working with method groups上有一个博客条目,与类似 - 但这里我们对返回类型不感兴趣,只对参数类型感兴趣。 other posts in his type inference series可能会有所帮助。
答案 3 :(得分:5)
请记住作业
Action<int> f = foo;
已经有很多语法糖。编译器实际上为此语句生成代码:
Action<int> f = new Action<int>(foo);
相应的方法调用编译没有问题:
bar(new Action<int>(foo));
Fwiw,帮助编译器推断类型参数也是如此:
bar<int>(foo);
所以归结为问题,为什么赋值语句中的糖而不是方法调用中的糖?我不得不猜测这是因为糖在分配中是明确的,只有一种可能的替代品。但是在方法调用的情况下,编译器编写者已经不得不处理重载解决问题。其规则非常详细。他们可能只是没有解决它。
答案 4 :(得分:0)
为了完整起见,这不是特定于C#:相同的VB.NET代码同样失败:
Imports System
Module Test
Sub foo(ByVal x As integer)
End Sub
Sub bar(Of T)(ByVal f As Action(Of T))
End Sub
Sub Main()
Dim f As Action(Of integer) = AddressOf foo ' I can do this
bar(f) ' and then do this
bar(AddressOf foo) ' but this does not work
End Sub
End Module
错误BC32050:无法推断出'Public Sub bar(Of T)的类型参数'T'(f As System.Action(Of T))'。