Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么可以';t C#从这个看似简单、显而易见的案例中推断类型_C# - Fatal编程技术网

为什么可以';t C#从这个看似简单、显而易见的案例中推断类型

为什么可以';t C#从这个看似简单、显而易见的案例中推断类型,c#,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

鉴于此代码:

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 _) { }
}
C类
{
C()
{
测试(A);//好的
Test((字符串a)=>{});//很好
测试((动作)A);//好
Test(A);//无法从用法推断类型参数!
}
静态空隙试验(动作a){}
空A(字符串{}
}
编译器抱怨
Test(A)
无法将
t
理解为
string

Test((string a) => {});
对我来说,这似乎是一个非常简单的例子,我发誓我在我编写的其他通用实用程序和扩展函数中依赖了更复杂的推理。我错过了什么

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


更新2:在工作列表中添加了更多示例。

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

  • 它必须推断您想要将代理转换为泛型委托

  • 它必须推断委托参数的类型

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


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

您传递的是方法的名称a。.Net framework可以将其转换为
操作
,但它是隐式的,不会对此负责


但是,方法名仍然不是显式的
操作
对象。因此,它不会将该类型推断为
操作类型。

在我看来,这似乎是一个恶性循环

Test
方法需要一个由泛型类型
Action
构造的委托类型的参数。您通过的是方法组
测试(a)
。这意味着编译器必须将参数转换为委托类型()

但是哪种委托类型?要知道委托类型,我们需要知道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); 
//委托由匿名方法创建,
//不需要方法组转换-编译器知道它的操作
测试((字符串a)=>{});
//参数的类型是显式设置的
试验((动作)A);
或类型参数已明确指定:

Test<string>(A); // compiler knows what type of delegate to convert A to
测试(A);//编译器知道要将代理转换为哪种类型
附言

这会失败,因为唯一适用的方法(
Test(Action)
)需要类型推断,而类型推断算法要求每个参数都是某种类型或匿名函数。(根据类型推断算法规范(§7.5.2)推断出这一事实)方法组
A
不是任何类型(即使它可以转换为适当的委托类型),并且它不是匿名函数

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

Test((Action<string>)A);
测试((动作)A);
这成功了,区别在于非类型化的方法组参数
A
被强制转换为一个类型,从而允许
Test
的类型推断以特定类型的表达式作为方法的唯一参数正常进行


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

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

的示例也会出现同样的问题,您使用的是哪个版本的C#?与C#3编译器相比,C#4编译器在方法组方面具有更好的类型推断。我验证了在C#4下这仍然是一个编译器错误。@Jon-但即使是C#4也不能正确解决这个问题,这是一个遗憾。很抱歉,我自己无法核实——我现在正在度假,只有酒店的笔记本电脑。我想我可以在上面安装.NET 4:)@Jon Skeet-我一刻也不相信你拥有一台没有安装.NET的笔记本电脑我觉得这听起来不错。我可以做
Test((stringa)=>{})
并且它可以很好地解析,或者Pierre Alain的
Te示例
Test((Action<string>)A);