我一直在研究Visual Studio 14 CTP和C#6.0以及使用零传播运算符.
但是,我找不到为什么以下代码无法编译.这些功能尚未记录,所以我不确定这是一个错误还是扩展方法,?.操作员不支持这些方法并且错误消息具有误导性.
class C
{
public object Get()
{
return null;
}
}
class CC
{
}
static class CCExtensions
{
public static object Get(this CC c)
{
return null;
}
}
class Program
{
static void Main(string[] args)
{
C c = null;
var cr = c?.Get(); //this compiles (Get is instance method)
CC cc = null;
var ccr = cc?.Get(); //this doesn't compile
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)
错误信息是:
'ConsoleApplication1.CC'不包含'Get'的定义,也没有扩展方法'Get'接受类型'ConsoleApplication1.CC'的第一个参数(你是否缺少using指令或汇编引用?)
VSa*_*dov 29
是.这是一个错误.谢谢你提出这个问题.该示例应该编译,并且应该导致条件调用Get,无论Get是否是扩展名.
使用"?".在cc?.Get()中表示调用者希望在进一步继续之前对cc进行空值检查.即使Get能以某种方式处理null,调用者也不希望这种情况发生.
Chr*_*ens 26
我不在罗斯林团队工作,但我相信这是一个错误.我看了一下源代码,我可以解释发生了什么.
首先,我不同意SLaks的回答,这是不支持的,因为扩展方法不会取消引用它们的this参数.这是一个毫无根据的要求,考虑到有任何的没有提到它的 设计 讨论.另外,运算符的语义变成了大致看起来像三元运算符((obj == null) ? null : obj.Member)的一些语义,因此从技术意义上说它不能得到支持的原因并不是很合理.我的意思是,当它归结为生成代码时,this实例方法的隐式和this静态扩展方法上的显式确实没有区别.
错误消息是一个很好的线索,这是一个错误,因为它抱怨该方法不存在,实际上它.您可能已经通过从调用中删除条件运算符,使用成员访问运算符,并使代码编译成功来测试了这一点.如果这是非法使用运营商,您将得到类似这样的消息:error CS0023: Operator '.' cannot be applied to operand of type '<type>'.
问题在于,当Binder试图将语法绑定到已编译的符号时,它使用的方法private static NameSyntax GetNameSyntax(CSharpSyntaxNode, out string) [link]无法返回尝试绑定调用表达式(我们的方法调用)时所需的方法名称.
可能的解决方法是case在GetNameSyntax[link]中为交换机添加一个额外的语句,如下所示(File:Compilers/CSharp/Source/Binder/Binder_Expressions.cs:2748):
// ...
case SyntaxKind.MemberBindingExpression:
return ((MemberBindingExpressionSyntax)syntax).Name;
// ...
Run Code Online (Sandbox Code Playgroud)
这可能被忽略了,因为调用扩展方法作为成员的语法(即使用成员访问运算符)使用与成员访问运算符和条件访问运算符一起使用的不同语法集,特别是?.运算符使用a MemberBindingExpressionSyntax不考虑该GetNameSyntax方法.
有趣的是,没有为第一个var cr = c?.Get();编译的方法填充方法名称.但是,它起作用,因为首先找到该类型的本地方法组成员并将其传递给BindInvocationExpression[ link ] 的调用.在解决方法时(注意在尝试ResolveDefaultMethodGroup[ link ]之前调用BindExtensionMethod[ link ]),它首先检查这些方法并找到它.在扩展方法的情况下,它尝试查找与传递给方法的方法名称匹配的扩展方法,在这种情况下,该方法名称是空字符串而不是Get,并导致显示错误的错误.
使用我的本地版本的Roslyn修复了我的bug,我得到了一个编译的程序集,其代码看起来像(使用dotPeek重新生成):
internal class Program
{
private static void Main(string[] args)
{
C c1 = (C) null;
object obj1 = c1 != null ? c1.Get() : (object) null;
CC c2 = (CC) null;
object obj2 = c2 != null ? CCExtensions.Get(c2) : (object) null;
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)