关于c#:使用null-coalescing运算符进行隐式转换

Implicit conversion with null-coalescing operator

我发现我的程序有一种奇怪的行为,经过进一步的分析,我发现我的C知识或其他地方可能有问题。我以为这是我的错,但我在任何地方都找不到答案…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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"的计算结果为空。我不明白为什么它是空的。

我在google上找到了这个问题的响应——隐式的空合并操作符结果的强制转换——符合官方规范。

但是按照这个规范,我找不到"b"为空的原因:(也许我读错了,在这种情况下,我为垃圾邮件道歉。

If A exists and is not a nullable type or a reference type, a compile-time error occurs.

…事实并非如此。

If b is a dynamic expression, the result type is dynamic. At run-time, a is first evaluated. If a is not null, a is converted to dynamic, and this becomes the result. Otherwise, b is evaluated, and this becomes the result.

…也不是这样。

Otherwise, if A exists and is a nullable type and an implicit conversion exists from b to A0, the result type is A0. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0, and this becomes the result. Otherwise, b is evaluated and converted to type A0, and this becomes the result.

…A存在,从B到A0的隐式转换不存在。

Otherwise, if A exists and an implicit conversion exists from b to A, the result type is A. At run-time, a is first evaluated. If a is not null, a becomes the result. Otherwise, b is evaluated and converted to type A, and this becomes the result.

…A存在,从B到A的隐式转换不存在。

Otherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0 (if A exists and is nullable) and converted to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.

…B有一个类型B,隐式转换存在于A到B之间。a的计算结果为空。因此,B应该被评估,B应该是结果。

Otherwise, a and b are incompatible, and a compile-time error occurs.
Does not happen

我错过什么了吗?


为什么期望空合并运算符返回new B()a不为空,所以a ?? new B()的计算结果为a

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

? Otherwise, if b has a type B and an implicit conversion exists from
a to B, the result type is B. At run-time, a is first evaluated. If a
is not null, a is unwrapped to type A0 (if A exists and is nullable)
and converted to type B, and this becomes the result. Otherwise, b is
evaluated and becomes the result.

存在从aB的隐式转换,因此B是表达式的结果类型。也就是说,a将被隐式铸造给B。隐式运算符返回null

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


Otherwise, if b has a type B and an implicit conversion exists from a
to B, the result type is B. At run-time, a is first evaluated. If a is
not null, a is unwrapped to type A0 (if A exists and is nullable) and
converted to type B, and this becomes the result. Otherwise, b is
evaluated and becomes the result.

…B的类型为B,存在从A到B的隐式转换。A是计算为空。因此,B应该被评估,B应该被评估结果。

< /块引用>

你把这个解释错了。没有说明在执行null检查之前,已经完成了aB的转换。说明转换前进行了null检查!

你的案子符合这个条件:

If a is not null, a is unwrapped to type A0 (if A exists and is
nullable) and converted to type B, and this becomes the
result.


好吧,规格说明说(我改为xy,这里不太混淆):

? Otherwise, if y has a type Y and an implicit conversion exists from x to Y, the result type is Y. At run-time, x is first evaluated. If x is not null, x is unwrapped to type X0 (if X exists and is nullable) and converted to type Y, and this becomes the result. Otherwise, y is evaluated and becomes the result.

这种情况发生了。首先,检查左侧的x,即a,查看null。但它本身并不是null。然后使用左侧。然后运行隐式转换。B型的结果是…null

请注意,这不同于:

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

在这种情况下,左操作数是一个表达式(x,本身就是null,结果变成了右操作数(y)。

也许引用类型之间的隐式转换应该返回null(如果和),仅当原始类型是null时,作为一个好的实践?

我想写规范的人可以这样做(但没有):

? Otherwise, if y has a type Y and an implicit conversion exists from x to Y, the result type is Y. At run-time, x is first evaluated and converted to type Y. If the output of that conversion is not null, that output becomes the result. Otherwise, y is evaluated and becomes the result.

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


我们需要关注的部分是空合并表达式的编译时类型。

Otherwise, if b has a type B and an implicit conversion exists from a to B, the result type is B. At run-time, a is first evaluated. If a is not null, a is unwrapped to type A0 (if A exists and is nullable) and converted to type B, and this becomes the result. Otherwise, b is evaluated and becomes the result.

要将其放入伪代码,请执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
    }
    ...
}