我应该从以下结果中对Equals(),ReferenceEquals()和==做出什么决定?它们实际上产生了什么?
#region
int integer = 1;
int integer2 = integer;
bool referenceEquality = (integer == integer2);//true
bool valueEquality = integer.Equals(integer2);//true
bool valueEqualityMore = object.Equals(integer, integer2);//true
bool valueEqualityMoreMore = object.ReferenceEquals(integer, integer2);//false
#endregion
#region
int integer = 1;
int integer2 = 1;
bool referenceEquality = (integer == integer2);//true
bool valueEquality = integer.Equals(integer2);//true
bool valueEqualityMore = object.Equals(integer, integer2);//true
bool valueEqualityMoreMore = object.ReferenceEquals(integer, integer2);//false
#endregion
#region
MyClass obj = new MyClass(1, "Hello");
MyClass obj2 = obj;
bool referenceEquality = (obj == obj2);//true
bool valueEquality = obj.Equals(obj2);//true
bool valueEqualityMore = object.Equals(obj, obj2);//true
bool valueEqualityMoreMore = object.ReferenceEquals(obj, obj2);//true
#endregion
#region
MyClass obj = new MyClass(1, "Hello");
MyClass obj2 = new MyClass(1, "Hello");
bool referenceEquality = (obj == obj2);//false
bool valueEquality = obj.Equals(obj2);//false
bool valueEqualityMore = object.Equals(obj, obj2);//false
bool valueEqualityMoreMore = object.ReferenceEquals(obj, obj2);//false
#endregion
地狱!我什么都不懂。
对我来说,第一个块的referenceEquals()应该返回true。 ==在第二个块中应返回false(因为引用不同)。 并且,第4个块中的两个Equals()都应该返回true(因为它们的值相同)。
答案 0 :(得分:8)
您似乎遇到的第一个混淆点是,对于值类型,即int
,float
,DateTime
,==
运算符是值相等。对于引用类型,==
(默认情况下,见下文)引用相等。这可以解释前两个整数情况下答案的差异。
其次,Equals()
的默认实现测试引用相等,而不是值相等。因此,MyClass
似乎没有覆盖Equals()
,这解释了您的参考案例之间答案的差异。
此外,许多引用类型(例如String
)会覆盖==
运算符以提供值相等语义。因此,最好的办法是,记住哪些类型是查找文档。
简而言之:
==
是值相等(对于框架类型)ReferenceEquals()
始终是引用相等==
默认是引用相等,但可以(通常是框架类型)重写以提供值相等Equals()
默认是引用相等,但可以(通常是框架类型)重写以提供值相等答案 1 :(得分:1)
ReferenceEquals
:如果两个对象指向内存中的相同位置,则它们是相同的。对于两种不同的值类型,情况绝不会这样。
Equals
:如果等于覆盖认为它们相等,则两个对象相等。这通常意味着如果ReferenceEqual将返回true,并且属性返回true。 Equals
每个对象或结构的行为通常不同。 注意:每个内置值类型(int,double,IntPtr)都覆盖了Equals
,这就是为什么它对于值类型(比较内容)然后与ReferenceEquals
(比较地址)的行为不同的
==
:如果两个对象具有相同的引用,或者两个值类型具有相同的内容,则返回true。盒装值类型在进行比较之前是未装箱的,首先将它们显式地转换为对象。
请注意,new MyClass(...)
与Equals
一起返回false。这是因为MyClass
的实现没有覆盖Equals
方法。结果:它的行为与ReferenceEquals
相同。
更新:在Equals
上添加了关于值类型的说明
答案 2 :(得分:1)
ReferenceEquals
检查两个对象引用是否引用同一对象。也就是说,它们指向内存中的相同位置。
Equals
是一个虚方法,因此实际上可以覆盖它来做任何事情。然而,该方法的目的是以对于类型有意义的方式比较实例,无论方式如何。如果未覆盖Equals
,则会使用object.Equals
的实现,这相当于ReferenceEquals
。
==
是等于运算符,默认情况下比较值类型实例的引用类型实例(即类,盒装值类型,接口)和值相等(字段的值相同)的引用相等性(即结构)。 ==
可以重载以提供自定义行为,但它不像Equals
那样虚拟。
将int
用作object
,例如将其传递给ReferenceEquals
类似object
参数的方法,将int
括起来,创建object.ReferenceEquals(integer1, integer2)
堆内的对象,里面有整数。 object.ReferenceEquals((object)integer1, (object)integer2)
基本上意味着ReferenceEquals
。
通过引用相等(==
或false
)比较同一整数的不同盒装实例将给出Equals
,而值相等(true
)将给出{{1 }}。对于作为值类型的未装箱的相同整数,==
和Equals
都会比较值相等,因此会给true
。
答案 3 :(得分:1)
默认情况下,operator==
在用作a==b
时的行为如下。请注意,operator==
可以被覆盖以执行任何操作。
a
是值类型,则编译为a.Equals(b)
。a
是引用类型,则编译为object.Equals(a,b)
。 object.Equals(a, b)
仅对引用类型起作用,并执行以下操作:
a
为null
,则返回(b==null)
a.Equals(b)
object.ReferenceEquals(a, b)
仅适用于引用类型。 C#/ .NET中的对象引用在内部保存为指针。如果引用a
和b
相同,即它们内部指向同一对象,则此方法返回true。
值类型以两种形式存在:未装箱和装箱。对于以下讨论值类型的情况,我将使用以下变量:
int unboxed = 3;
// boxing occurs automatically when a value type is cast to object or
// to an interface. This allocates memory on the heap to store the value
// and places a reference (internally a pointer) to this memory in boxed.
object boxed = unboxed;
此时,boxed
表现为引用类型。两个盒装值可能不在内存中的同一位置,正如您在object.ReferenceEquals(integer1, integer2)
调用中看到的那样(由于每个参数转换为object
而发生自动装箱)。当您致电object.Equals(integer1, integer2)
时,值会被加框,但由于integer1
的方框形式不为空,因此调用的行为类似于integer1.Equals((object)integer2)
,因为框内值会返回true
都是1
。 (请参阅下面的说明,为什么我在这里有手动演员)。
关于上述手动广告的说明:System.Int32
以及大多数其他值类型都有方法Int32.Equals(Int32 other)
,因此调用integer1.Equals(integer2)
不会包含integer2
的值。这对轻量级值类型的性能有很大(正面)影响。
答案 4 :(得分:1)
我对实际进行的比较做了更正。我为所有情况添加了相同的比较,因为你只对整数(使用==运算符)执行.Equals(int),而不是.Equals(对象)和其他类型。我还为参数添加了显式铸件,以显示使用它们作为参数引起的隐式铸件:
int integer = 1;
int integer2 = 1; // exactly the same result as copying integer
bool valueEquality = (integer == integer2); //true
bool valueEquality2 = integer.Equals(integer2); //true
bool typeAndValueEquality = integer.Equals((object)integer2); //true
bool typeAndValueEquality2 = object.Equals((object)integer, (object)integer2); //true
bool referenceEquality = object.ReferenceEquals((object)integer, (object)integer2); //false
//
MyClass obj = new MyClass(1, "Hello");
MyClass obj2 = obj;
bool referenceEquality = (obj == obj2); //true
bool referenceEquality2 = obj.Equals((object)obj2); //true
bool typeAndReferenceEquality = object.Equals((object)obj, (object)obj2); //true
bool referenceEquality3 = object.ReferenceEquals((object)obj, (object)obj2); //true
//
MyClass obj = new MyClass(1, "Hello");
MyClass obj2 = new MyClass(1, "Hello");
bool referenceEquality = (obj == obj2); //false
bool referenceEquality2 = obj.Equals((object)obj2); //false
bool typeAndReferenceEquality = object.Equals((object)obj, (object)obj2); //false
bool referenceEquality = object.ReferenceEquals((object)obj, (object)obj2); //false