我只是在查看this answer,其中包含来自.NET Reflector的Nullable<T>
代码,我注意到两件事:
Nullable<T>
转到T
时,需要进行显式转换。==
运算符。鉴于这两个事实,令我惊讶的是这编译:
int? value = 10;
Assert.IsTrue(value == 10);
使用代码value == 10
,value
被神奇地转换为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>
的各种运算符在看起来不被定义时如何工作?
答案 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的拳击语义非常奇怪,并且融入了运行时。没有办法模仿它们。
is
,as
和合并运算符的可空语义都被用于语言。
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);
}
看起来有从10
到Nullable<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代码。