C#.Equals(),.ReferenceEquals()和==运算符

我对这三个人的理解是:

  • .Equals()testing数据是否相等(缺less更好的描述)。 .Equals()可以为同一个对象的不同实例返回True,这是最常用的重写方法。

  • .ReferenceEquals()testing两个对象是否是同一个实例,不能被覆盖。

  • ==默认情况下与ReferenceEquals()相同,但是这个可以被覆盖。

但是C#站点状态:

在对象类中, EqualsReferenceEquals方法在语义上是等价的,只不过ReferenceEquals只对对象实例起作用。 ReferenceEquals方法是静态的。

现在我不明白。 任何人都可以点亮这个?

混淆的根源似乎是C#站点提取出来的拼写错误,它应该是:“…除了Equals只能用于对象实例,ReferenceEquals方法是静态的。


你对每个语义的差异都是松散的(虽然“同一个对象的不同实例”似乎有点混淆,它应该可能读为“同一types的不同实例”),而且可能被忽略。

如果我们把它放在一边,让我们来处理你的问题的最后一点,即他们如何处理普通的System.Object实例和System.Object引用(我们需要避开==的非多态性)。 在这里,所有这三个操作都会起到相当的作用 ,但是有一个警告: Equals不能在null上被调用。

Equals是一个实例方法,它接受一个参数( 可以null )。 由于它是一个实例方法(必须在实际对象上调用),因此无法在null引用上调用它。

ReferenceEquals是一个静态方法,它接受两个参数,其中任何一个都可以为null 。 由于它是静态的(不与对象实例关联),所以在任何情况下都不会抛出NullReferenceException

==是一个运算符,在这种情况下( object )的行为与ReferenceEquals相同。 它不会抛出一个NullReferenceException

为了显示:

 object o1 = null; object o2 = new object(); //Technically, these should read object.ReferenceEquals for clarity, but this is redundant. ReferenceEquals(o1, o1); //true ReferenceEquals(o1, o2); //false ReferenceEquals(o2, o1); //false ReferenceEquals(o2, o2); //true o1.Equals(o1) //NullReferenceException o1.Equals(o2) //NullReferenceException o2.Equals(o1) //false o2.Equals(o2) //true 

看看这个关于这个主题的MSDN文章 。

我认为有关的观点是:

要检查引用相等性,请使用ReferenceEquals。 要检查值是否相等,请使用Equals或Equals。

默认情况下,运算符==通过确定两个引用是否指示相同的对象来testing引用是否相等,因此引用types不需要实现operator ==以获得此function。 当一个types是不可变的时,意味着实例中包含的数据不能被改变,重载operator ==来比较值相等性而不是引用相等可能是有用的,因为作为不可变对象,只要它们具有相同的价值。

希望这可以帮助!

您对.ReferenceEquals的理解是正确的。

.Equals检查值types的数据相等性,以及非值types(一般对象)的引用相等性。

。对象可以被覆盖,以执行某种forms的数据相等性检查

编辑:此外,.ReferenceEquals不能用于值types(以及它可以,但将永远是假的)

在Object类中.Equals实现标识,而不是相等。 它检查引用是否相等。 代码可能是这样的:

 public virtual Boolean Equals(Object other) { if (this == other) return true; return false; } 

在您的类中实现.Equals时,只有在基类不是Object的情况下才应调用基类.Equals。 是的,那很复杂。

更重要的是,由于派生类可以覆盖.Equals,所以你不能调用它来检查标识微软添加静态.ReferenceEquals方法。

如果你使用一些类,然后逻辑 .Equals检查相等性和.ReferenceEquals检查身份。

想要添加我的五分钱与“空”比较。

  1. ReferenceEquals (object,object)和“(object)arg1 == arg2”是一样的(所以在值types的情况下,你得到了装箱,这需要时间)。 但是,这种方法是在几种情况下检查你的参数为null的唯一100%安全的方法

    • a)通过呼叫成员之前。 操作者
    • b)检查AS运营商的结果。
  2. ==和Equals() 。 为什么我说ReferenceEquals是100%安全的空检查? 想象一下,你在核心交叉项目库中编写generics扩展,并假设你将generics参数types限制为某个域types。 这个types可以引入“==”运算符 – 现在或以后(相信我,我已经看到了很多,这个运算符可以有一个非常“奇怪的”逻辑,尤其是如果涉及到域或持久化对象)。 你试着检查你的参数为null,然后调用它的成员操作。 惊喜,你可以在这里有NullRef。 因为==运算符几乎与Equals()相同 – 非常自定义且非常不可预测。 有一个区别,但应该考虑到 – 如果你不限制你的generics参数为一些自定义types(==只能用于你的types是“类”),==运算符是相同的对象.ReferenceEquals(..)。 等于实现总是使用最终types,因为它是虚拟的。

所以我的build议是,当你编写自己的types或从知名types派生,你可以使用==检查为空。 否则使用object.ReferenceEquals(arg,null)。

我在Ani的优秀回答中展示了处理引用types和重载平等方法时的主要差异。

  • 你可以在这里看到这个代码的工作版本: https : //dotnetfiddle.net/dFKMhB
  • 或者,将此代码粘贴到LinqPad并以Language: C# Program

 void Main() { //odd os are null; evens are not null object o1 = null; object o2 = new object(); object o3 = null; object o4 = new object(); object o5 = o1; object o6 = o2; Demo d1 = new Demo(Guid.Empty); Demo d2 = new Demo(Guid.NewGuid()); Demo d3 = new Demo(Guid.Empty); Debug.WriteLine("comparing null with null always yields true..."); ShowResult("ReferenceEquals(o1, o1)", () => ReferenceEquals(o1, o1)); //true ShowResult("ReferenceEquals(o3, o1)", () => ReferenceEquals(o3, o1)); //true ShowResult("ReferenceEquals(o5, o1)", () => ReferenceEquals(o5, o1)); //true ShowResult("o1 == o1", () => o1 == o1); //true ShowResult("o3 == o1", () => o3 == o1); //true ShowResult("o5 == o1", () => o5 == o1); //true Debug.WriteLine("...though because the object's null, we can't call methods on the object (ie we'd get a null reference exception)."); ShowResult("o1.Equals(o1)", () => o1.Equals(o1)); //NullReferenceException ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException ShowResult("o3.Equals(o1)", () => o3.Equals(o1)); //NullReferenceException ShowResult("o3.Equals(o2)", () => o3.Equals(o2)); //NullReferenceException ShowResult("o5.Equals(o1)", () => o5.Equals(o1)); //NullReferenceException ShowResult("o5.Equals(o2)", () => o5.Equals(o1)); //NullReferenceException Debug.WriteLine("Comparing a null object with a non null object always yeilds false"); ShowResult("ReferenceEquals(o1, o2)", () => ReferenceEquals(o1, o2)); //false ShowResult("ReferenceEquals(o2, o1)", () => ReferenceEquals(o2, o1)); //false ShowResult("ReferenceEquals(o3, o2)", () => ReferenceEquals(o3, o2)); //false ShowResult("ReferenceEquals(o4, o1)", () => ReferenceEquals(o4, o1)); //false ShowResult("ReferenceEquals(o5, o2)", () => ReferenceEquals(o3, o2)); //false ShowResult("ReferenceEquals(o6, o1)", () => ReferenceEquals(o4, o1)); //false ShowResult("o1 == o2)", () => o1 == o2); //false ShowResult("o2 == o1)", () => o2 == o1); //false ShowResult("o3 == o2)", () => o3 == o2); //false ShowResult("o4 == o1)", () => o4 == o1); //false ShowResult("o5 == o2)", () => o3 == o2); //false ShowResult("o6 == o1)", () => o4 == o1); //false ShowResult("o2.Equals(o1)", () => o2.Equals(o1)); //false ShowResult("o4.Equals(o1)", () => o4.Equals(o1)); //false ShowResult("o6.Equals(o1)", () => o4.Equals(o1)); //false Debug.WriteLine("(though again, we can't call methods on a null object:"); ShowResult("o1.Equals(o2)", () => o1.Equals(o2)); //NullReferenceException ShowResult("o1.Equals(o4)", () => o1.Equals(o4)); //NullReferenceException ShowResult("o1.Equals(o6)", () => o1.Equals(o6)); //NullReferenceException Debug.WriteLine("Comparing 2 references to the same object always yields true"); ShowResult("ReferenceEquals(o2, o2)", () => ReferenceEquals(o2, o2)); //true ShowResult("ReferenceEquals(o6, o2)", () => ReferenceEquals(o6, o2)); //true <-- Interesting ShowResult("o2 == o2", () => o2 == o2); //true ShowResult("o6 == o2", () => o6 == o2); //true <-- Interesting ShowResult("o2.Equals(o2)", () => o2.Equals(o2)); //true ShowResult("o6.Equals(o2)", () => o6.Equals(o2)); //true <-- Interesting Debug.WriteLine("However, comparing 2 objects may yield false even if those objects have the same values, if those objects reside in different address spaces (ie they're references to different objects, even if the values are similar)"); Debug.WriteLine("NB: This is an important difference between Reference Types and Value Types."); ShowResult("ReferenceEquals(o4, o2)", () => ReferenceEquals(o4, o2)); //false <-- Interesting ShowResult("o4 == o2", () => o4 == o2); //false <-- Interesting ShowResult("o4.Equals(o2)", () => o4.Equals(o2)); //false <-- Interesting Debug.WriteLine("We can override the object's equality operator though, in which case we define what's considered equal"); Debug.WriteLine("eg these objects have different ids, so we treat as not equal"); ShowResult("ReferenceEquals(d1,d2)",()=>ReferenceEquals(d1,d2)); //false ShowResult("ReferenceEquals(d2,d1)",()=>ReferenceEquals(d2,d1)); //false ShowResult("d1 == d2",()=>d1 == d2); //false ShowResult("d2 == d1",()=>d2 == d1); //false ShowResult("d1.Equals(d2)",()=>d1.Equals(d2)); //false ShowResult("d2.Equals(d1)",()=>d2.Equals(d1)); //false Debug.WriteLine("...whilst these are different objects with the same id; so we treat as equal when using the overridden Equals method..."); ShowResult("d1.Equals(d3)",()=>d1.Equals(d3)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect) ShowResult("d3.Equals(d1)",()=>d3.Equals(d1)); //true <-- Interesting (sort of; different to what we saw in comparing o2 with o6; but is just running the code we wrote as we'd expect) Debug.WriteLine("...but as different when using the other equality tests."); ShowResult("ReferenceEquals(d1,d3)",()=>ReferenceEquals(d1,d3)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method) ShowResult("ReferenceEquals(d3,d1)",()=>ReferenceEquals(d3,d1)); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method) ShowResult("d1 == d3",()=>d1 == d3); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method) ShowResult("d3 == d1",()=>d3 == d1); //false <-- Interesting (sort of; same result we had comparing o2 with o6; but shows that ReferenceEquals does not use the overridden Equals method) Debug.WriteLine("For completeness, here's an example of overriding the == operator (wihtout overriding the Equals method; though in reality if overriding == you'd probably want to override Equals too)."); Demo2 d2a = new Demo2(Guid.Empty); Demo2 d2b = new Demo2(Guid.NewGuid()); Demo2 d2c = new Demo2(Guid.Empty); ShowResult("d2a == d2a", () => d2a == d2a); //true ShowResult("d2b == d2a", () => d2b == d2a); //false ShowResult("d2c == d2a", () => d2c == d2a); //true <-- interesting ShowResult("d2a != d2a", () => d2a != d2a); //false ShowResult("d2b != d2a", () => d2b != d2a); //true ShowResult("d2c != d2a", () => d2c != d2a); //false <-- interesting ShowResult("ReferenceEquals(d2a,d2a)", () => ReferenceEquals(d2a, d2a)); //true ShowResult("ReferenceEquals(d2b,d2a)", () => ReferenceEquals(d2b, d2a)); //false ShowResult("ReferenceEquals(d2c,d2a)", () => ReferenceEquals(d2c, d2a)); //false <-- interesting ShowResult("d2a.Equals(d2a)", () => d2a.Equals(d2a)); //true ShowResult("d2b.Equals(d2a)", () => d2b.Equals(d2a)); //false ShowResult("d2c.Equals(d2a)", () => d2c.Equals(d2a)); //false <-- interesting } //this code's just used to help show the output in a friendly manner public delegate bool Statement(); void ShowResult(string statementText, Statement statement) { try { Debug.WriteLine("\t{0} => {1}",statementText, statement()); } catch(Exception e) { Debug.WriteLine("\t{0} => throws {1}",statementText, e.GetType()); } } class Demo { Guid id; public Demo(Guid id) { this.id = id; } public override bool Equals(object obj) { return Equals(obj as Demo); //if objects are of non-comparable types, obj will be converted to null } public bool Equals(Demo obj) { if (obj == null) { return false; } else { return id.Equals(obj.id); } } //if two objects are Equal their hashcodes must be equal //however, if two objects hash codes are equal it is not necessarily true that the objects are equal //ie equal objects are a subset of equal hashcodes //more info here: https://stackoverflow.com/a/371348/361842 public override int GetHashCode() { return id.GetHashCode(); } } class Demo2 { Guid id; public Demo2(Guid id) { this.id = id; } public static bool operator ==(Demo2 obj1, Demo2 obj2) { if (ReferenceEquals(null, obj1)) { return ReferenceEquals(null, obj2); //true if both are null; false if only obj1 is null } else { if(ReferenceEquals(null, obj2)) { return false; //obj1 is not null, obj2 is; therefore false } else { return obj1.id == obj2.id; //return true if IDs are the same; else return false } } } // NB: We also HAVE to override this as below if overriding the == operator; this is enforced by the compiler. However, oddly we could choose to override it different to the below; but typically that would be a bad idea... public static bool operator !=(Demo2 obj1, Demo2 obj2) { return !(obj1 == obj2); } } 

Equals()根据基础types(Value / Reference)和ReferenceEquals() Equals()检查散列码或等价性,以检查散列码。 如果两个对象指向相同的内存位置,则ReferenceEquals返回true

 double e = 1.5; double d = e; object o1 = d; object o2 = d; Console.WriteLine(o1.Equals(o2)); // Ture Console.WriteLine(Object.Equals(o1, o2)); // Ture Console.WriteLine(Object.ReferenceEquals(o1, o2)); // False Console.WriteLine(e.Equals(d)); // Ture Console.WriteLine(Object.Equals(e, d)); // Ture Console.WriteLine(Object.ReferenceEquals(e, d)); // False