我发现了我的程序的一个奇怪的行为,经过进一步的分析,我发现在我的C#知识或其他地方可能有问题。我相信这是我的错,但我无法在任何地方找到答案......
public class B
{
public static implicit operator B(A values)
{
return null;
}
}
public class A { }
public class Program
{
static void Main(string[] args)
{
A a = new A();
B b = a ?? new B();
//b = null ... is it wrong that I expect b to be B() ?
}
}
此代码中的变量“b”被计算为null。我不明白为什么它是空的。
我用Google搜索并在此问题中找到了回复 - Implicit casting of Null-Coalescing operator result - 与官方规范一致。
但是按照这个规范,我找不到“b”为空的原因:(也许我读错了,在这种情况下,我为垃圾邮件道歉。
如果A存在且不是可空类型或引用类型,则会发生编译时错误。
......事实并非如此。
如果b是动态表达式,则结果类型是动态的。在运行时,首先评估a。如果a不为null,则a将转换为dynamic,这将成为结果。否则,将评估b,这将成为结果。
......事实并非如此。
否则,如果A存在且是可空类型,并且从b到A0存在隐式转换,则结果类型为A0。在运行时,首先评估a。如果a不为null,则打开a以键入A0,这将成为结果。否则,b被评估并转换为类型A0,这就是结果。
...存在,从b到A0的隐式转换不存在。
否则,如果A存在且从b到A存在隐式转换,则结果类型为A.在运行时,首先计算a。如果a不为null,则a成为结果。否则,b被评估并转换为类型A,这就是结果。
...存在,从b到A的隐式转换不存在。
否则,如果b具有类型B并且从a到B存在隐式转换,则结果类型为B.在运行时,首先计算a。如果a不为null,则打开a以键入A0(如果A存在并且可以为空)并转换为类型B,这将成为结果。否则,b被评估并成为结果。
... b有一个B型,从a到B存在隐式转换。 a被评估为null。因此,应该评估b,结果应该是b。
否则,a和b不兼容,并发生编译时错误。 不会发生
我错过了什么吗?
答案 0 :(得分:8)
为什么期望null-coalescing运算符返回new B()
? a
不为空,因此a ?? new B()
的评估结果为a
。
现在我们知道会返回a
,我们需要确定结果的类型(T
)以及我们是否需要将a
投射到T
•否则,如果b具有类型B并且存在隐式转换 a到B,结果类型为B. 在运行时,首先评估a。如果一个 不是null, a 解包到类型A0(如果A存在且可以为空) 并且转换为B类,这就是结果。否则,b是 评估并成为结果。
从A
到B
存在隐式转换,因此B
是表达式的结果类型。这意味着a
将隐式投放到B
。你的隐式运算符返回null
。
事实上,如果你写var b = a ?? new B();
(注意var
),你会看到编译器推断B
是表达式返回的类型。
答案 1 :(得分:4)
你正在解释这个错误。在执行否则,如果b具有类型B并且存在隐式转换 对于B,结果类型为B.在运行时,首先评估a。如果是的话 不为null,a被解包为类型A0(如果A存在且可以为空)和 转换为B型,这就成了结果。否则,b是 评估并成为结果。
... b具有类型B,并且从a到B存在隐式转换.a是 评估为null。因此,b应该被评估,b应该被评估 结果。
a
检查之前,没有任何内容表明B
到null
转换已完成。它指出null
检查在转换之前完成!
你的情况适合:
如果 a不为空,则打开a以键入A0(如果A存在且是 nullable)和转换为B型,这个成为 结果强>
答案 2 :(得分:1)
嗯,规范说明了(我改为x
和y
以减少混淆):
•否则,如果y具有类型Y并且存在从x到Y的隐式转换,则结果类型为Y.在运行时,首先计算x。如果x不为null,则x被解包为类型X0(如果X存在并且可以为空)并转换为类型Y,这就成为结果。否则,y被评估并成为结果。
这种情况发生了。首先,检查x
的左侧a
是否为null
。null
。但它本身并不是B
。然后使用左侧 。然后运行隐式转换。类型null
的结果是...... A a = new A();
B b = (B)a ?? new B();
。
请注意,这与:
不同x
在这种情况下,左操作数是一个表达式{null
),其本身是y
,结果变为右侧(null
)。
参考类型之间的隐式转换应该只返回null
(if和),只有在原始版本为null
的情况下,这是一个好习惯吗?
我想编写规范的人可以这样做(但没有):
•否则,如果y具有类型Y并且存在从x到Y的隐式转换,则结果类型为Y.在运行时,首先计算x并将其转换为类型Y.如果该转换的输出为非null,该输出成为结果。否则,y被评估并成为结果。
也许那会更直观?无论转换的输入是否为null → null
,它都会强制运行时调用隐式转换。如果典型的实现快速确定{{1}}。
答案 3 :(得分:0)
我们需要看的部分是null-coalescing表达式的编译时类型。
否则,如果b具有类型B并且从a到B存在隐式转换,则结果类型为B.在运行时,首先计算a。如果a不为null,则打开a以键入A0(如果A存在并且可以为空)并转换为类型B,这将成为结果。否则,b被评估并成为结果。
将其置于伪代码中:
public Tuple<Type, object> NullCoalesce<TA, TB>(TA a, TB b)
{
...
else if (a is TB) // pseudocode alert, this won't work in actual C#
{
Type type = typeof(TB);
object result;
if (a != null)
{
result = (TB)a; // in your example, this resolves to null
}
else
{
result = b;
}
return new Tuple<Type, object>(type, result);
}
...
}