价值与参考

时间:2009-10-18 15:50:15

标签: c# reference

我应该从以下结果中对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(因为它们的值相同)。

5 个答案:

答案 0 :(得分:8)

您似乎遇到的第一个混淆点是,对于值类型,即intfloatDateTime==运算符是值相等。对于引用类型,==(默认情况下,见下文)引用相等。这可以解释前两个整数情况下答案的差异。

其次,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)仅对引用类型起作用,并执行以下操作:

  • 如果anull,则返回(b==null)
  • 否则请致电a.Equals(b)

object.ReferenceEquals(a, b)仅适用于引用类型。 C#/ .NET中的对象引用在内部保存为指针。如果引用ab相同,即它们内部指向同一对象,则此方法返回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