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

时间:2011-06-03 15:28:33

标签: c#

鉴于此代码:

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)无法确定Tstring

这对我来说似乎是一个非常简单的案例,我发誓我在其他通用实用程序和扩展函数中依赖于更复杂的推理。我在这里缺少什么?

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

更新2:在可行的列表中添加了更多示例。

5 个答案:

答案 0 :(得分:35)

Test(A);

这是失败的,因为唯一适用的方法(Test<T>(Action<T>))需要类型推断,类型推断算法要求每个参数都属于某种类型或者是匿名函数。 (这个事实是从类型推断算法(第7.5.2节)的规范推断出来的)方法组A不属于任何类型(即使它可以转换为适当的委托类型),它不是一个匿名函数。

Test<string>(A);

这成功了,区别在于类型推断不是绑定Test所必需的,方法组A可以转换为所需的委托参数类型void Action<string>(string)

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

这成功了,不同之处在于类型推断算法在第一阶段(第7.5.2.1节)中提供了匿名函数。匿名函数的参数和返回类型是已知的,因此可以进行显式参数类型推断,从而在匿名函数(void ?(string))中的类型和委托类型中的类型参数之间建立相应的参数。 Test方法的参数(void Action<T>(T))。没有为与匿名函数的此算法对应的方法组指定算法。

Test((Action<string>)A);

这成功了,区别在于将无类型方法组参数A强制转换为类型,从而允许Test的类型推断正常进行,并将特定类型的表达式作为唯一该方法的论据。

理论上我没理由为什么不能在方法组A上尝试重载解析。然后 - 如果找到单个最佳绑定 - 方法组可以被赋予与匿名函数相同的处理。在这样的情况下尤其如此,其中方法组恰好包含一个候选项并且它没有类型参数。但它在C#4中不起作用的原因似乎是这个功能没有设计和实现。鉴于此功能的复杂性,应用程序的狭隘性以及三种简单的解决方法的存在,我不会为此屏住呼吸!

答案 1 :(得分:8)

我认为这是因为这是一个两步推理:

  • 必须推断您要将A转换为通用委托

  • 必须推断委托参数的类型应该是什么

我不确定这是否是原因,但我的预感是,对于编译器来说,两步推理并不一定容易。


编辑:

只是预感,但有些事告诉我第一步就是问题。编译器必须计算转换为具有不同数量的泛型参数的委托 ,因此无法推断出参数的类型。

答案 2 :(得分:5)

这对我来说似乎是一个恶性循环。

Test方法需要从泛型类型Action<T>构造的委托类型参数。您传递了方法组Test(A)。这意味着编译器必须将您的参数转换为委托类型(method group conversion)。

但是哪种委托类型?要知道我们需要知道的委托类型T.我们没有明确指定它,因此编译器必须推断它以找出委托类型。

要推断方法的类型参数,我们需要知道方法参数的类型,在本例中是委托类型。编译器不知道参数类型,因此失败。

在所有其他情况下,任何一种论证都是显而易见的:

// 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); 
明确指定

或类型参数:

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

P.S。 more on type inference

答案 3 :(得分:2)

您正在传递方法的名称A. .Net框架可以将其转换为Action,但它是隐含的,它不会对此负责。

但是,方法名称仍然是 NOT 明确的Action<>对象。因此,它不会将类型推断为Action类型。

答案 4 :(得分:2)

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