【发布时间】:2011-09-09 13:18:48
【问题描述】:
注意:这似乎已在 Roslyn 中修复
这个问题是在我写给this one 的答案时出现的,它谈到了null-coalescing operator 的关联性。
提醒一下,null-coalescing 运算符的想法是表单的表达式
x ?? y
首先评估x,然后:
- 如果
x的值为 null,则计算y,这就是表达式的最终结果 - 如果
x的值不为空,则y的值不计算,x的值是表达式的最终结果,经过转换为编译- 时间类型y如有必要
现在通常不需要转换,或者只是从可空类型到不可空类型 - 通常类型是相同的,或者只是从(比如)int? 到int。但是,您可以创建自己的隐式转换运算符,并在必要时使用这些运算符。
对于x ?? y 的简单情况,我没有看到任何奇怪的行为。但是,对于 (x ?? y) ?? z,我看到了一些令人困惑的行为。
这是一个简短但完整的测试程序 - 结果在 cmets 中:
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# 编译器时,我非常犹豫是否要喊“错误”,但我对发生的事情感到困惑......
编辑:好的,这是一个更糟糕的例子,感谢配置器的回答,这让我有进一步的理由认为这是一个错误。编辑:该示例现在甚至不需要两个空合并运算符...
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' -
另请注意,使用 Linq 表达式编译相同代码时不会发生这种情况。
-
@Peter 不太可能的模式,但对
(("working value" ?? "user default") ?? "system default")来说似乎是合理的 -
@yes123:当它只处理转换时,我并不完全相信。看到它两次执行一个方法,很明显这是一个错误。您会惊讶于某些看起来不正确但实际上完全正确的行为。 C# 团队比我聪明 - 我倾向于认为我是愚蠢的,直到我证明某些事情是他们的错。
标签: c# null-coalescing-operator