当==未定义时,为什么==运算符适用于Nullable ?
我只是看着这个答案 ,其中包含来自.NET Reflector的Nullable
的代码,我注意到两件事:
- 从
Nullable
到T
时需要显式转换。 -
==
运算符未定义。
鉴于这两个事实,令我惊讶的是,这编译:
int? value = 10; Assert.IsTrue(value == 10);
使用代码value == 10
,任何一个value
都被神奇地转换为int
(因此允许使用int
的==
运算符,或者为Nullable
神奇地定义了==
运算符。(或者,我认为不太可能,Reflector省略了一些代码。)
我希望必须做以下其中一项:
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
这些当然是有效的,但==
也有效,而这是我没有得到的部分。
我之所以注意到这一点并且问这个问题的原因是我正在尝试编写一个与Nullable
类似的结构。 我从上面链接的Reflector代码开始,只做了一些非常小的修改。 不幸的是,我的CustomNullable
工作方式不同。 我无法做Assert.IsTrue(value == 10)
。 我得到“运算符==
不能应用于CustomNullable
和int
类型的操作数”。
现在,无论修改多么微小,我都不希望能做到……
CustomNullable value = null;
…因为我知道Nullable
背后有一些编译器魔术允许将值设置为null
即使Nullable
是一个结构,但我希望我能够模仿所有其他行为Nullable
如果我的代码(几乎)写得相同。
任何人都可以了解Nullable
的各种运算符在看起来不被定义时如何工作?
鉴于这两个事实,令我惊讶的是,这编译
鉴于这两个事实,这是令人惊讶的。
这是第三个事实: 在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都为非null,则为true或false。 这就是它在VB中的工作方式,但不适用于C#。
我希望我能够模仿
Nullable
所有其他行为,如果我的代码是(几乎)相同的。
你将不得不学会失望,因为你的期望完全不符合现实。 Nullable
是一种非常特殊的类型 ,它的神奇属性深深地嵌入在C#语言和运行时中。 例如:
-
C#自动将运营商提升为可空。 没有办法说“自动将操作员提升到MyNullable”。 通过编写自己的用户定义的运算符,您可以非常接近。
-
C#具有空文字的特殊规则 – 您可以将它们分配给可空变量,并将它们与可空值进行比较,编译器会为它们生成特殊代码。
-
nullables的拳击语义非常奇怪,并且融入运行时。 没有办法模仿它们。
-
is
,as
和coalescing运算符的Nullable语义被烘焙到语言中。 -
Nullables不满足
struct
约束。 没有办法模仿。 -
等等。
好吧,如果你可以使用reflection器,为什么不编译这段代码:
int? value = 10; Console.WriteLine(value == 10);
然后用reflection器打开它? 你会看到这个(确保选择’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一起使用时的含义,并相应地编译必要的检查。
这取决于语言。 在处理可空值类型的运算符时,C#和Visual Basic会发出不同的代码。 要想了解它,你需要查看实际的IL代码。
Nullable
有这种方法 :
public static implicit operator T?(T value) { return new T?(value); }
它看起来像是从10
到Nullable
的隐式转换
让== /!=为你工作。 你可以加
public static bool operator ==(MyNullable left, MyNullable right) {} // together with: public static implicit operator MyNullable (T value) {}
应该支持myNullable == 10
操作
因为编译器将Nullable
转换为T
然后执行比较。