我有两个(同一类型的)对象,其中包含myprop
类型的道具byte?
。属性设置为null
。当我执行objectA.myprop.Equals(objectB.myprop)
时,我得到'true',尽管MSDN代码示例声明“应用于任何null对象的Equals返回false。”
我猜C#使用单独的重载进行可空类型比较。我想知道在这种情况下C#如何在内部处理对象与可空类型。
答案 0 :(得分:5)
当你这样称呼它时,它会使用Nullable<T>.Equals(object)
来显示文档中的预期行为:
(如果......,则返回值为true
HasValue
属性为false,另一个参数为null
。也就是说,根据定义,两个空值相等。
同样通过==
进行平等,C#4规范(提升运算符)的7.3.7节说明:
对于等于运算符
==
[和]!=
,如果操作数类型都是非可空值类型且结果类型为bool,则存在提升形式的运算符。提升形式是通过添加一个?每个操作数类型的修饰符。 提升的运算符认为两个空值等于,并且空值不等于任何非空值。如果两个操作数都是非null,则提升的运算符将解包操作数并应用基础运算符来生成bool结果。
(强调我的。)
编辑:现在我实际上不同意这里接受的答案,它说它不是一般规则。就object.Equals
:的实施而言, 是一般规则
以下语句必须适用于Equals方法的所有实现。在列表中,x,y和z表示非空的对象引用。
[...]
x.Equals(null)
返回false。
因此,虽然 是一般规则,但它并不适用于此特定情况 - 因为此处的值不是对象引用 - 它是值类型值。它仍然有点令人惊讶,除非你基本上接受Nullable<T>
有点特殊情况 - 它具有特定的C#编译器支持和它在拳击方面具有CLR支持开箱。
答案 1 :(得分:4)
为了清晰起见,我对此进行了编辑。
你误解了这句话。给出完整的背景:
string s = null;
string t = String.Empty; // Logically the same as ""
// Equals applied to any null object returns false.
bool b = (t.Equals(s));
这表示对string
对象的引用不等同于null
引用。您将该引用脱离了上下文,并将其解释为一般规则,但事实并非如此。但是,这是指引用不可为空的类型。
当你处理可以为空的原始类型时:
实际规则是
两个可为null的可空类型的相等比较 评估为真。
答案 2 :(得分:2)
Nullable类型是一个结构,结构永远不会得到一个null 值,所以当一个可空类型等于null时,这意味着这个变量实际上不是null而是隐式的强制转换< / strong>发生并且该变量使用属性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
答案 3 :(得分:0)
我认为 Nullable
比较 X==Y
的行为完全符合直觉预期,但我感觉这里可能对 X.Equals(Y)
产生误解,它的行为好像是 {{1 }} 方法调用。
static
引用“应用于任何空对象的相等返回 false。” 对于 非静态 方法 object A, B;
MyStruct? X, Y;
为真,并且通常应适用于任何明智地覆盖它。
然而,唯一的原因是,A.Equals(B)
是属于 A.Equals
的非静态方法,这意味着 A
永远不可能是 { {1}}。用 A
尝试 null
确实会抛出 A.Equals(B)
。
之所以如此,是因为A = null
引用的实例可能比声明的变量具有更具体的类型 {{1 }},而这种更具体的类型又可能会覆盖 NullReferenceException
方法。换句话说,如果 A
为 null,则运行时不知道要使用 A
的哪个实现,因此必须抛出 A.Equals
。
然而,静态方法不能被覆盖,这意味着 A
可以是 A.Equals
因为要运行的方法在编译时就已经知道了——时间。 (这就是为什么调用静态方法比调用非静态方法要快一点,也是为什么编译器可能决定内联一些静态调用,但我离题了。)
因此,对于任何 static 实现,两个操作数都可以是 Exception
。此外,A
应始终被视为与 null
相同,并且与任何其他值不同。 (如果 null
的实现偏离了这个常见的假设,那么这通常会被视为错误。)
静态 null
方法示例:
null
在这方面,Equals
的行为方式与对象相同。唯一的区别是 Equals
支持 X 或 Y 或两者为空而不抛出异常。所以更准确地说,它的行为类似于上面提到的静态相等方法,这通常更可取。
但是 A==B;
A!=B;
MyClass.Equals(A, B);
object.Equals(A, B);
ReferenceEquals(A, B);
怎么可能表现得像一个静态方法?这是因为 Nullable
是一个结构体。 C#中的Structs不支持继承,所以变量的类型和实例的类型没有区别。这就是为什么运行时永远不会怀疑 Equals 指的是哪个方法。所以不需要异常。因此,该结构支持的任何值都可以为两个操作数的方法所支持,因此 X.Equals(Y)
只是另一个值。
现在,除此之外,还有一些对语法糖和 X.Equals(Y)
类型优化的内置特殊支持,但据我所知,行为仍然存在,如上所述。