为什么C#不能从这个看似简单明显的例子中推断出types

鉴于此代码:

class C { C() { Test<string>(A); // fine Test((string a) => {}); // fine Test((Action<string>)A); // fine Test(A); // type arguments cannot be inferred from usage! } static void Test<T>(Action<T> a) { } void A(string _) { } } 

编译器抱怨Test(A)不能把T看作string

这对我来说似乎是一个相当简单的例子,我发誓我在其他通用实用程序和扩展函数中依赖于我写的更复杂的推理。 我在这里错过了什么?

更新1:这是在C#4.0编译器中。 我在VS2010中发现了这个问题,上面的示例来自我在LINQPad 4中创build的一个最简单的情况。

更新2:添加一些更多的例子到什么工作的清单。

 Test(A); 

这是因为唯一适用的方法( Test<T>(Action<T>) )需要types推断,并且types推断algorithm要求每个参数都是某种types或者是一个匿名函数。 (这个事实是根据types推断algorithm(§7.5.2)的规范推断的)方法组A没有任何types(即使它可以转换成合适的委托types),也不是一个匿名函数。

 Test<string>(A); 

这成功了,区别在于types推断不需要绑定Test,而方法组A可转换为所需的委托参数typesvoid Action<string>(string)

 Test((string a) => {}); 

这成功了,区别在于types推断algorithm在第一阶段(§7.5.2.1)为匿名函数提供了规定。 匿名函数的参数和返回types是已知的,因此可以进行明确的参数types推断,并且由此在匿名函数( void ?(string) )中的types与委托types中的types参数Test方法的参数( void Action<T>(T) )。 没有为匿名函数对应此algorithm的方法组指定algorithm。

 Test((Action<string>)A); 

这成功了,不同之处在于非types方法组参数A被转换为一个types,从而允许Test的types推断通常以特定types的expression式作为该方法的唯一参数。

在理论上我无法理解为什么在方法组A上不能尝试重载parsing。 然后,如果find一个最佳绑定,那么方法组可以被赋予与匿名函数相同的处理方式。 在这种情况下尤其如此,方法组只包含一个候选者,而且没有types参数。 但是它在C#4中不起作用的原因似乎是这个特性没有被devise和实现。 考虑到这个function的复杂性,它的应用的简单性,以及三个简单的解决scheme的存在,我不会屏住呼吸!

我想这是因为这是一个两步推断:

  • 它必须推断你想将A转换为一个通用的委托

  • 它必须推断委托参数的types应该是什么

我不确定这是否是这个原因,但是我的预感是编译器对两步推理不是一件容易的事情。


编辑:

只是一个预感,但有些事情告诉我,第一步是问题。 编译器必须找出转换为具有不同数量的通用参数的委托,因此它不能推断参数的types。

这看起来像是一个恶性循环。

Test方法期望从通用typesAction<T>构造的委托types的参数。 你传入一个方法组代替: Test(A) 。 这意味着编译器必须将您的参数转换为委托types( 方法组转换 )。

但是,哪种代表types? 要知道委托types,我们需要知道T.我们没有明确指定它,所以编译器必须推断它来找出委托types。

为了推断方法的types参数,我们需要知道方法参数的types,在这里是委托types。 编译器不知道参数types,从而失败。

在所有其他情况下,无论哪种types的论据都是显而易见

 // delegate is created out of anonymous method, // no method group conversion needed - compiler knows it's Action<string> Test((string a) => {}); // type of argument is set explicitly Test((Action<string>)A); 

或者明确指定types参数:

 Test<string>(A); // compiler knows what type of delegate to convert A to 

PS 更多的types推断

您传递方法 A的名称.Net框架可以将其转换为一个Action ,但它是隐含的,它不会承担责任。

但是,方法名不是一个显式的Action<>对象。 因此,它不会将该types推断为Actiontypes。

我可能是错的,但我想像C#不能推断types的真正原因是由于方法重载和产生的模糊性。 例如,假设我有以下方法: void foo (int)void foo (float) 。 现在,如果我写var f = foo 。 编译器应该select哪个foo ? 同样,使用Test(foo)示例也会出现同样的问题。