当==未定义时,为什么==运算符适用于Nullable <t>?</t>

时间:2012-01-26 01:32:03

标签: c# operators operator-overloading nullable

我只是在查看this answer,其中包含来自.NET Reflector的Nullable<T>代码,我注意到两件事:

  1. Nullable<T>转到T时,需要进行显式转换。
  2. 未定义==运算符。
  3. 鉴于这两个事实,令我惊讶的是这编译:

    int? value = 10;
    Assert.IsTrue(value == 10);
    

    使用代码value == 10value被神奇地转换为int(因此允许使用int的{​​{1}}运算符,或者==运算符被神奇地定义为==。(或者,我认为不太可能,Reflector会遗漏一些代码。)

    我希望必须执行以下操作之一:

    Nullable<int>

    这些当然有效,但Assert.IsTrue((value.Equals(10)); // works because Equals *is* defined Assert.IsTrue(value.Value == 10); // works because == is defined for int Assert.IsTrue((int?)value == 10); // works because of the explicit conversion 也有效,而这是我没有得到的部分。

    我注意到这一点的原因是我正在尝试编写一个与==类似的结构。我从上面链接的Reflector代码开始,并做了一些非常小的修改。不幸的是,我Nullable<T>的工作方式不同。我无法CustomNullable<T>。我得到“操作符Assert.IsTrue(value == 10)无法应用于==CustomNullable<int>类型的操作数”。

    现在,无论修改多么微小,我都不希望能够做到......

    int

    ...因为我知道CustomNullable<T> value = null; 背后有一些编译器魔术允许将值设置为Nullable<T>,即使null是一个结构,但我期望我能够模仿Nullable<T>的所有其他行为,如果我的代码(几乎)完全相同的话。

    任何人都可以了解Nullable<T>的各种运算符在看起来不被定义时如何工作?

5 个答案:

答案 0 :(得分:27)

  

鉴于这两个事实,令我惊讶的是,这编译了

只考虑这两个事实,这是令人惊讶的。

以下是第三个事实:在C#中,大​​多数运营商都被“提升为可空”

通过“提升为可空”,我的意思是,如果你说:

int? x = 1;
int? y = 2;
int? z = x + y;

然后你得到的语义是“如果x或y为null,则z为null。如果两者都不为null,则添加它们的值,转换为nullable,并将结果赋值给z。”

平等也是如此,尽管平等有点奇怪,因为在C#中,平等仍然只是双值。要正确解除,平等应该是三值的:如果x或y为null,则x == y应 null ,如果x和y都为非空,则为true或false。这就是它在VB中的工作方式,但不适用于C#。

  

如果我的代码(几乎)写得相同,我希望我能够模仿Nullable<T>的所有其他行为。

你将不得不学会以失望的方式生活,因为你的期望与现实完全不符。 Nullable<T>是一种非常特殊的类型,它的神奇属性深深嵌入在C#语言和运行时中。例如:

  • C#自动将运营商提升为可空。没有办法说“自动将操作员提升到MyNullable”。通过编写自己的用户定义的运算符,可以非常接近。

  • C#对null文字有特殊规则 - 你可以将它们分配给可空变量,并将它们与可空值进行比较,编译器为它们生成特殊代码。

  • nullables的拳击语义非常奇怪,并且融入了运行时。没有办法模仿它们。

  • isas和合并运算符的可空语义都被用于语言。

  • Nullables不满足struct约束。没有办法效仿。

  • 等等。

答案 1 :(得分:3)

好吧,如果你可以使用反射器,为什么不编译这段代码:

int? value = 10;
Console.WriteLine(value == 10);

然后在反射器中打开它?你会看到这一点(确保选择'None'作为.net版本进行反编译):

int? value;
int? CS$0$0000;
&value = new int?(10);
CS$0$0000 = value;
Console.WriteLine((&CS$0$0000.GetValueOrDefault() != 10) ? 0 : &CS$0$0000.HasValue);

所以基本上编译器会为你做繁重的工作。它理解'=='操作在与nullables一起使用时的含义,并相应地编译必要的检查。

答案 2 :(得分:1)

因为编译器converts Nullable<T> to T然后执行比较。

答案 3 :(得分:1)

Nullable<T>this method

public static implicit operator T?(T value)
{
  return new T?(value);
}

看起来有从10Nullable<int> 10

的隐式转换

让== /!=为你工作。你可以添加

public static bool operator ==(MyNullable<T> left, MyNullable<T> right) {}
// together with:
public static implicit operator MyNullable<T>(T value) {}

应该支持myNullable == 10操作

答案 4 :(得分:1)

这取决于语言。在处理可空值类型的运算符时,C#和Visual Basic会发出不同的代码。要想了解它,你需要查看实际的IL代码。