【问题标题】:Value vs Reference价值与参考
【发布时间】:2009-10-19 00:30:12
【问题描述】:

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

【问题讨论】:

  • 您能否缩小您的问题范围,具体来说:1) 您对这些结果中的哪些感到惊讶 2) 您在谈论什么决定
  • 好评。查看更新。
  • referenceEquals 比较对象实例引用,因为 int 值是两个不同对象的框,它们具有不同的引用。 == 默认情况下比较引用,因此 MyClass 的两个实例,即使具有相同的状态,也有不同的引用,因为它们是两个不同的对象。
  • 另外,这个帖子的副本:stackoverflow.com/questions/772953/…

标签: c# reference


【解决方案1】:

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

其次,Equals() 的默认实现测试引用相等,而不是值相等。因此,既然MyClass 似乎没有覆盖Equals(),这就解释了您的参考案例之间答案的差异。

此外,许多引用类型(例如 String)会覆盖 == 运算符以提供值相等语义。因此,在您记住哪些类型是哪些类型之前,最好的办法是查找文档。

简而言之:

  • 值类型
    • == 是价值平等(对于框架类型)
    • 不是引用类型,所以引用相等是没有意义的
  • 引用类型
    • ReferenceEquals() 始终是引用相等
    • == 默认情况下是引用相等,但可以(并且通常用于框架类型)覆盖以提供值相等
    • Equals() 默认情况下是引用相等,但可以(并且通常用于框架类型)覆盖以提供值相等

【讨论】:

  • 可以在这里找到对所有这些的一个很好的更新:stackoverflow.com/questions/384294/…,它显示了引擎盖下发生的 C++ 代码。基本上,它明确表明值类型由Equals 特殊处理。
【解决方案2】:

ReferenceEquals:如果两个对象指向内存中的相同位置,则它们是相同的。两种不同的值类型永远不会出现这种情况。

Equals: 如果 Equals 覆盖认为两个对象相等,则两个对象相等。这通常意味着 ReferenceEqual 是否会返回 true,并且属性是否返回 true。 Equals 每个对象或结构的行为通常不同。 注意:每个内置值类型(int、double、IntPtr)都覆盖了Equals,这就是为什么它对值类型(比较内容)和ReferenceEquals(比较地址)表现不同的原因。

==: 如果两个对象具有相同的引用,或者两个值类型具有相同的内容,则返回 true。装箱的值类型在比较之前被拆箱,除非它们首先显式地转换为对象。

请注意,您的 new MyClass(...) 返回 false 和 Equals。那是因为MyClass 的实现没有覆盖Equals 方法。结果:它的行为与ReferenceEquals 相同。

更新:在 Equals 上添加了值类型的注释

【讨论】:

  • 积分 ReferenceEquals() 发生了什么?
  • 而且,为什么,int integer = 1;整数 2 = 1; bool referenceEquality = (integer == integer2);//true
  • == 的行为与直接调用 ReferenceEquals 不同。对于值类型,它的行为与调用 Equals 相同。
【解决方案3】:

ReferenceEquals 检查两个对象引用是否引用同一个对象。也就是说,它们指向内存中的相同位置。

Equals 是一个虚拟方法,所以在实践中它可以被覆盖来做任何事情。然而,该方法的目的是以对类型有意义的方式比较实例,无论是哪种方式。如果Equals没有被覆盖,则使用object.Equals的实现,相当于ReferenceEquals

== 是相等运算符,默认情况下比较引用类型实例(即类、装箱值类型、接口)的引用相等性和值类型实例(即结构体)的值相等性(字段值相同) )。 == 可以重载以提供自定义行为,但它不像 Equals 那样是虚拟的。

int 用作object,例如通过将其传递给具有object 参数的ReferenceEquals 之类的方法,将int 装箱,从而在堆上创建一个包含整数的对象。 object.ReferenceEquals(integer1, integer2) 基本上是指object.ReferenceEquals((object)integer1, (object)integer2)

通过引用相等(ReferenceEquals==)比较同一整数的不同装箱实例将得到 false,而值相等(Equals)将得到 true。对于未装箱的相同整数,它们是值类型,==Equals 都比较值相等,因此将给出 true

【讨论】:

    【解决方案4】:

    默认情况下,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。

    值类型以两种形式存在:unboxedboxed。对于以下讨论值类型的案例,我将使用以下变量:

    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 的值装箱。这对轻量值类型的性能有很大(积极的)影响。

    【讨论】:

      【解决方案5】:

      我对所做的实际比较进行了更正。我为所有情况添加了相同的比较,因为您只对整数(使用 == 运算符)执行 .Equals(int),而不是对其他类型执行 .Equals(object)。我还为参数添加了显式转换,以显示哪些隐式转换是由使用它们作为参数引起的:

      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
      

      【讨论】:

      • @Guffa,非常好。如果您之前发布过,我已经接受了您的回答。
      猜你喜欢
      • 2022-06-21
      • 1970-01-01
      • 2015-02-06
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      • 2016-10-17
      • 1970-01-01
      • 2016-03-31
      相关资源
      最近更新 更多