使用null-coalescing运算符进行隐式转换

时间:2014-01-27 17:20:07

标签: c# null implicit-conversion

我发现了我的程序的一个奇怪的行为,经过进一步的分析,我发现在我的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不兼容,并发生编译时错误。   不会发生

我错过了什么吗?

4 个答案:

答案 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是   评估并成为结果。

AB存在隐式转换,因此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检查之前,没有任何内容表明Bnull转换已完成。它指出null检查在转换之前完成

你的情况适合:

  

如果 a不为空,则打开a以键入A0(如果A存在且是   nullable)和转换为B型,这个成为   结果

答案 2 :(得分:1)

嗯,规范说明了(我改为xy以减少混淆):

  

•否则,如果y具有类型Y并且存在从x到Y的隐式转换,则结果类型为Y.在运行时,首先计算x。如果x不为null,则x被解包为类型X0(如果X存在并且可以为空)并转换为类型Y,这就成为结果。否则,y被评估并成为结果。

这种情况发生了。首先,检查x的左侧a是否为nullnull。但它本身并不是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);
    }
    ...
}