首页 文章

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

提问于
浏览
6

我发现了我的程序的一个奇怪的行为,经过进一步的分析,我发现在我的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 . 我不明白为什么它是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 回答

  • 4

    那么,规范说(我改为 xy ,以减少混淆):

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

    有时候是这样的 . 首先,检查左侧 x (仅为 anull . 但它本身并不是 null . 然后左侧 is 被使用 . 然后运行隐式转换 . 其类型 B 的结果是...... null .

    请注意,这与以下内容不同:

    A a = new A();
        B b = (B)a ?? new B();
    

    在这种情况下,左操作数是一个表达式( x ),其本身是 null ,结果变为右侧( y ) .

    也许引用类型之间的隐式转换应该返回 null (if和)仅当原始是 null 时,这是一个好习惯吗?


    我想那些编写规范的人可能会这样做(但没有):

    •否则,如果y具有类型Y并且存在从x到Y的隐式转换,则结果类型为Y.在运行时,首先计算x并将其转换为类型Y.如果该转换的输出不为null ,输出成为结果 . 否则,y被评估并成为结果 .

    也许那会更直观?无论转换的输入是否为 null ,它都会强制运行时调用隐式转换 . 如果典型的实现快速确定 null → null ,那应该不会太昂贵 .

  • 1

    为什么期望null-coalescing运算符返回 new B()a 不为空,因此 a ?? new B() 的计算结果为 a .

    既然我们知道将返回 a ,我们需要确定结果的类型( T )以及是否需要将 a 转换为 T .

    •否则,如果b具有类型B并且从a到B存在隐式转换,则结果类型为B.在运行时,首先计算a . 如果a不为null,则打开a以键入A0(如果A存在且为可以为空)并转换为B型,这就成了结果 . 否则,b被评估并成为结果 .

    AB 存在隐式转换,因此 B 是表达式的结果类型 . 这意味着 a 将隐式投放到 B . 你的隐式运算符返回 null .

    实际上,如果您编写 var b = a ?? new B(); (注意 var ),您将看到编译器推断 B 是表达式返回的类型 .

  • 0

    否则,如果b具有类型B并且从a到B存在隐式转换,则结果类型为B.在运行时,首先计算a . 如果a不为null,则打开a以键入A0(如果A存在且可为空)并转换为类型B,这将成为结果 . 否则,b被评估并成为结果 . ... b具有类型B,并且从a到B存在隐式转换.a被评估为null . 因此,应评估b,结果应为b .

    你在解释这个错误 . 没有什么说在执行 null 检查之前完成了 aB 转换 . 它声明 null 检查完成 before 转换!

    你的情况适合那个:

    如果a不为null,则打开a以键入A0(如果A存在且可为空)并转换为类型B,这将成为结果 .

  • 8

    我们需要看的部分是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);
        }
        ...
    }
    

相关问题