注意:这似乎已在罗斯林(Roslyn)中修复
在写我对这个问题的答案时,出现了这个问题,它讨论了null结点运算符的结合性。
提醒一下,null-coalescing运算符的想法是该形式的表达式
x ?? y
首先评估x
,然后:
- 如果的值为
x
null,y
则求值,这是表达式的最终结果 - 如果值
x
是非空,y
则不评估,的值x
是表达的最终结果,转换到编译时间类型的后y
如果需要的话
现在通常不需要转换,或者只是从可为null的类型到不可为null的类型的转换-通常类型是相同的,或者只是从(say)int?
到int
。但是,您可以创建自己的隐式转换运算符,并在必要时使用它们。
对于的简单情况x ?? y
,我还没有看到任何奇怪的行为。但是,随着(x ?? y) ?? z
我看到一些令人困惑的行为。
这是一个简短但完整的测试程序-结果在注释中:
using System;
public struct A
{
public static implicit operator B(A input)
{
Console.WriteLine("A to B");
return new B();
}
public static implicit operator C(A input)
{
Console.WriteLine("A to C");
return new C();
}
}
public struct B
{
public static implicit operator C(B input)
{
Console.WriteLine("B to C");
return new C();
}
}
public struct C {}
class Test
{
static void Main()
{
A? x = new A();
B? y = new B();
C? z = new C();
C zNotNull = new C();
Console.WriteLine("First case");
// This prints
// A to B
// A to B
// B to C
C? first = (x ?? y) ?? z;
Console.WriteLine("Second case");
// This prints
// A to B
// B to C
var tmp = x ?? y;
C? second = tmp ?? z;
Console.WriteLine("Third case");
// This prints
// A to B
// B to C
C? third = (x ?? y) ?? zNotNull;
}
}
因此,我们有三个自定义值类型,A
,B
和C
,与从A转换到B,A至C和B到C.
我可以理解第二种情况和第三种情况...但是为什么在第一种情况下会有额外的A到B转换?特别是,我真的希望第一种情况和第二种情况是相同的-毕竟,这只是将表达式提取到局部变量中。
发生什么情况的任何参与者?当涉及到C#编译器时,我非常想哭“ bug”,但是我对发生的事情感到困惑……
编辑:好的,由于配置程序的回答,这是正在发生的一个更糟糕的例子,这使我有更多理由认为这是一个错误。编辑:该示例现在甚至不需要两个null运算符...
using System;
public struct A
{
public static implicit operator int(A input)
{
Console.WriteLine("A to int");
return 10;
}
}
class Test
{
static A? Foo()
{
Console.WriteLine("Foo() called");
return new A();
}
static void Main()
{
int? y = 10;
int? result = Foo() ?? y;
}
}
输出为:
Foo() called
Foo() called
A to int
在Foo()
这里被两次调用的事实令我非常惊讶-我看不出任何理由两次对表达式求值。
C? first = ((B?)(((B?)x) ?? ((B?)y))) ?? ((C?)z);
。您将获得:Internal Compiler Error: likely culprit is 'CODEGEN'
(("working value" ?? "user default") ?? "system default")