C# 方法内动态方法返回值的显式强制转换不';不允许调用扩展方法
下面的代码演示了我的问题:C# 方法内动态方法返回值的显式强制转换不';不允许调用扩展方法,c#,c#-4.0,dynamic,C#,C# 4.0,Dynamic,下面的代码演示了我的问题: 公共类动态示例 { 公共无效剂量测定法() { var x=新的ExpandoObject(); 动态d=x; d、 GetString=(Func)(()=>“某些值”); d、 GetString().SomeStringExtension();//不工作-应为 ((string)d.GetString()).SomeStringExtension();//有效-应为 Build(d).SomeStringExtension();//不工作-意外? } 私有静态字
公共类动态示例
{
公共无效剂量测定法()
{
var x=新的ExpandoObject();
动态d=x;
d、 GetString=(Func)(()=>“某些值”);
d、 GetString().SomeStringExtension();//不工作-应为
((string)d.GetString()).SomeStringExtension();//有效-应为
Build(d).SomeStringExtension();//不工作-意外?
}
私有静态字符串生成(动态d)
{
return(string)d.GetString();
}
}
公共静态类扩展
{
公共静态int-SomeStringExtension(此字符串为s)
{
返回s.长度;
}
}
问题是,为什么编译器在将类型内联转换为扩展方法调用和将类型外联转换为单独的方法之间存在差异?如果在VS2010中将鼠标悬停在
Build(d)
上,您将看到整个表达式被认为是动态的,并在运行时解析。因此,它不能绑定到扩展方法(否则会在编译时发生)
整个表达式之所以是动态的,是因为在不知道参数的编译时类型的情况下,无法执行重载解析,因此方法的返回类型也不知道。Build(d)
仍然是一个动态表达式-该方法的编译时类型是动态的
,即使您可以确切地看到发生了什么。这意味着扩展方法将不起作用
基本上,编译器遵循相当简单的规则来确定表达式的类型,几乎任何涉及动态的表达式最终都被视为动态表达式。例外情况如下:
d是某种类型的
(总是被认为是bool
)
- 直接和使用
as
就我记忆所及,虽然我可能错了
现在,该语言的设计可以使这种情况静态地解决对Build
的调用,作为唯一合理的调用-毕竟,d
不可能是任何类型的,这将改变调用的方法-但是指定确切的规则将使语言规范化(和编译器)要复杂得多,但收获相对较少。+1作为一个补充问题:为什么在var s=Build(d);
中var
是动态的?(我要补充的是,这可能是真正的问题。如果Build(d)的结果
是隐式动态的,则无法在运行时解析SomeStringExtension
)它不起作用,因为dynamic
告诉编译器停止对变量进行类型分析。如果没有这些信息,您希望它如何匹配扩展方法?explicate cast起作用,因为您显式强制转换为已知的编译时类型。同意。我怀疑编译器决定ild(dynamic d)方法必须服从于动态链接,但我不理解原因。(将编辑问题以摆脱多个构建方法-以保持这些注释有效的方式。)@dlev OP认为,在这种情况下,动态与对象类似,方法解析是在编译时而不是在运行时完成的。因此,dynamic c=“Hello”
;function with multipleoverload(c);
不会选择“最正确”的重载,而是等同于object c=“Hello”;
。我将添加“我也是”:-@xanatos在运行时运行一个小型版本的编译器,在方法重载解析发生之前执行动态解析。或者至少是类似的,我对确切的细节有点模糊。它完全在规范中列出。所以你可以使用方法的重载(如果存在构建的重载,那么在运行时会选择正确的方法),但是你会失去扩展方法的使用,对吗?@xanatos:如果没有强制转换或其他转换到“已知”类型,是的。我知道Jon离这类问题不会太远!我之所以接受这个答案,是因为它解释了cast的处理方式不同,从而解释了为什么最近两次调用扩展方法的尝试之间存在差异。在这种情况下,可以执行重载解析,因为编译器可以注意到这个类中有一个可访问的方法,它是唯一可访问的方法,并且对于参数类型始终有效。但这是一个相对罕见的案例。这就是为什么我想在这里使用“不是”而不是“不能”的原因:)@JonSkeet它的实例比Build
作为唯一可访问的方法更为罕见,实际上,因为string
是密封的,所以从技术上讲,Build
的结果只能具有名为SomeStringExtension
的扩展方法。如果sayBuild
返回了一个未密封的类型,那么它仍然可以返回一个只有运行时才知道的子类,其实例方法名为SomeStringExtension
,然后使用正确的后期绑定行为Build(d).SomeStringExtension()
可以工作,但是如果编译器假定这是规则的一个例外,并在编译时解决了Build
调用,那么它将错过它。