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