byte?
的属性myprop
。这些属性被设置为null
。当我执行objectA.myprop.Equals(objectB.myprop)
时,结果为“true”,尽管MSDN代码示例表明:“应用于任何 null 对象的 Equals 返回 false。”我猜测C#在可空类型比较中使用了单独的重载。我想知道在这种情况下C#是如何内部处理有对象和可空类型之分的。
Nullable<T>.Equals(object)
:
(如果返回值是true
,则意味着...)
同样对于通过
HasValue
属性为false,并且另一个参数为null
。也就是说,根据定义,两个null值相等。
==
进行的相等性检查,在C# 4规范(提升运算符)的第7.3.7节中说明:
(强调我自己加的。)对于相等性运算符
==
[和]!=
如果操作数类型都是非空值类型,并且结果类型是bool,则存在一个操作符的提升形式。提升形式是通过将单个?修改器添加到每个操作数类型来构造的。提升运算符认为两个null值相等,而null值与任何非null值不等。如果两个操作数都是非null值,则提升运算符展开操作数并应用基础运算符以产生bool结果。
object.Equals
的一般规则:
因此,虽然这是一个一般规则,但它不适用于这种特定情况——因为这里的值不是对象引用,而是值类型值。这仍然有些令人惊讶,除非你基本上接受对于所有 Equals 方法的实现,以下语句必须为真。在列表中,x、y 和 z 表示不为 null 的对象引用。
[...]
x.Equals(null)
返回 false。
Nullable<T>
是一个有点特殊的情况——它具有特定的 C# 编译器支持和 CLR 支持,涉及装箱和拆箱。string s = null;
string t = String.Empty; // Logically the same as ""
// Equals applied to any null object returns false.
bool b = (t.Equals(s));
可空类型是一个结构体,而结构体永远不会得到null值,因此当一个可空类型等于null时,这意味着该变量并非真正的null,而是发生了隐式转换,将该变量转换为一个值类型(可空类型)并赋予Value=null属性。所以:
int? a=null;//(a) get a memory space with value property = null
a.GetHashcode();//if (a) really is null must throw a exception but not throw
我认为 Nullable
比较 X==Y
的行为与直觉预期完全一致,但我感觉在 X.Equals(Y)
方面可能存在误解,它的行为就像是一个 static
方法调用。
object A, B;
MyStruct? X, Y;
"Equals applied to any null object returns false."这句话适用于非静态方法A.Equals(B)
,并且通常应该适用于任何合理的重写。
然而,唯一的原因是A.Equals
是属于A
的非静态方法,这意味着A
永远不可能为null
。如果使用A=null
尝试A.Equals(B)
,确实会抛出NullReferenceException
。
之所以必须如此,是因为由A
引用的实例可能比声明变量A
更具体化,而这个更具体化的类型可能反过来覆盖A.Equals
方法。换句话说,如果A
为空,则运行时不知道要使用哪个A.Equals
的实现,因此必须抛出异常。
然而,静态方法无法被覆盖,这意味着在编译时已知要运行的方法,所以A
可以是null
。(这就是为什么调用静态方法比调用它们的非静态对应方法稍微快一些,也是编译器可能决定内联一些静态调用的原因,但我在岔开话题。)
因此,对于任何静态实现,两个操作数都可以是null
。此外,null
应始终被视为与null
相同,并且与任何其他值不同。(如果Equals
的实现偏离了这个常见假设,那么通常将被认为是一个错误。)
静态Equals
方法的示例:
A==B;
A!=B;
MyClass.Equals(A, B);
object.Equals(A, B);
ReferenceEquals(A, B);
在这方面,Nullable
的行为与对象相同。唯一的区别是X.Equals(Y)
支持X或Y或两者都为空而不会抛出异常。因此,更正确的说法是它的行为类似于上面提到的静态等式方法,这通常更可取。
但是,X.Equals(Y)
如何像静态方法一样运作呢?这是因为Nullable
是一个结构体。C#中的结构体不支持继承,因此变量的类型和实例的类型之间没有区别。这就是为什么运行时永远不会怀疑Equals指的是哪个方法。因此,不需要异常。因此,任何由结构体支持的值都可以由该方法支持两个操作数,并且null
只是另一个值。
现在,除此之外,还有一些内置的特殊支持语法糖和优化Nullable
类型的方法,但是行为仍然保持我上面描述的那样,据我所知。
HasValue
属性,也为我解开了一些疑惑。不过还是感谢你为我澄清了这个问题!=) - ChrisNullable<T>
。请参阅我的答案以获取更多详细信息。 - Jon Skeet