注意:这似乎已经修复了Roslyn
这个问题是在写我对这一个的回答时出现的,它讨论了空合并操作符的结合律。
提醒一下,空合并操作符的思想是窗体的表达式
x ?? y
首先计算x
,然后:
x
的值为null,则计算y
,这是表达式的最终结果x
的值是非空的,则y
是计算的不,而x
的值是表达式的最终结果,在必要时转换为y
的编译时类型之后现在,通常不需要转换,或者它只是从一个可空的类型到一个不可空的类型-通常类型是相同的,或者只是从(说)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”,但我被难住了,不知道发生了什么……
编辑:好吧,这里有一个正在发生的事情的更糟糕的例子,多亏了配置器的答案,这让我进一步有理由认为这是一个bug。编辑:示例现在甚至不需要两个空合并操作符了…
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()
在这里被调用了两次,这让我非常惊讶——我看不出表达式有任何理由被评估调用两次。