Мое понимание этих трех было:
.Equals()
тесты на равенство данных (из-за отсутствия лучшего описания)..Equals()
может возвращать True для разных экземпляров одного и того же объекта, и это наиболее часто переопределяемый метод..ReferenceEquals()
проверяет, являются ли два объекта одним и тем же экземпляром и не могут быть переопределены.==
то же самое, что иReferenceEquals()
по умолчанию, но это МОЖЕТ быть переопределено.
Но станция C # утверждает:
В классе объектов символы
Equals
иReferenceEquals
методы семантически эквивалентны, за исключением того, чтоReferenceEquals
работает только на экземпляры объектов.ReferenceEquals
Метод является статическим.
Теперь я этого не понимаю. Может кто-нибудь пролить некоторый свет на это?
Ответы:
Источником вашего замешательства, похоже, является опечатка в отрывке из C # station, который должен гласить: «... за исключением того, что Equals работает только с экземплярами объектов. Метод ReferenceEquals является статическим».
Вы примерно правы насчет различий в семантических значениях каждого (хотя «разные экземпляры одного и того же объекта» кажутся немного запутанными, вероятно, следует читать «разные экземпляры одного и того же типа» ) и о том, какие из них можно переопределить.
Если оставить это в стороне, давайте разберемся с последней частью вашего вопроса, т.е. как они работают с простыми
System.Object
экземплярами иSystem.Object
ссылками (нам нужно и то, и другое, чтобы избежать неполиморфной природы==
). Здесь все три операции будут работать одинаково , но с одной оговоркой:Equals
их нельзя вызыватьnull
.Equals
- это метод экземпляра, который принимает один параметр (который может бытьnull
). Поскольку это метод экземпляра (должен вызываться для реального объекта), его нельзя вызвать поnull
-reference.ReferenceEquals
- это статический метод, который принимает два параметра, оба из которых могут бытьnull
. Поскольку он статичен (не связан с экземпляром объекта ), он не будет генерировать aNullReferenceException
ни при каких обстоятельствах.==
- оператор, который в данном случае (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
источник
.Equals()
)?object
классе» . Я думаю, вы пропустили эту часть? Потому что иначе вы бы не говорили о его переопределении.object
класса.Equals
также является статическим методом,object
принимающим два параметра. Тогда может быть один или обаnull
.Взгляните на эту статью MSDN по этому поводу.
Я думаю, уместные моменты:
Надеюсь это поможет!
источник
Ваше понимание .ReferenceEquals верное.
.Equals проверяет равенство данных для типов значений и равенство ссылок для типов, не являющихся значениями (общие объекты).
.Equals могут быть переопределены для объектов для выполнения некоторой формы проверки равенства данных.
РЕДАКТИРОВАТЬ: Кроме того, .ReferenceEquals не может использоваться для типов значений (ну, это может, но всегда будет ложным)
источник
Хочу добавить свои пять центов о сравнении с "нулем".
ReferenceEquals (object, object) - это то же самое, что и "(object) arg1 == arg2" (поэтому в случае типов значений вы получаете бокс, и это требует времени). Но этот метод - единственный на 100% безопасный способ проверить ваш аргумент на null в нескольких ситуациях, например
== и Equals (). Почему я говорю, что ReferenceEquals на 100% безопасен с проверкой нуля? Представьте, что вы пишете общие расширения в основных кросс-проектных библиотеках, и допустим, что вы ограничиваете общий тип параметра некоторым типом домена. Этот тип может вводить оператор «==» - сейчас или позже (и поверьте мне, я много видел, этот оператор может иметь очень «странную» логику, особенно если это касается объектов домена или персистентности). Вы пытаетесь проверить свой аргумент на нуль, а затем вызвать для него операцию члена. Сюрприз, здесь МОЖЕТ быть NullRef. Потому что оператор == почти такой же, как Equals () - очень нестандартный и очень непредсказуемый. Однако есть разница, которую следует учитывать - если вы не ограничиваете общий параметр каким-либо настраиваемым типом (== можно использовать, только если ваш тип - "класс"), оператор == совпадает с object . ReferenceEquals (..). Реализация Equals всегда используется из final типа, так как она виртуальная.
Поэтому я рекомендую, когда вы пишете свои собственные типы или производите от известных типов, вы можете использовать == для проверки на null. В противном случае используйте object.ReferenceEquals (arg, null).
источник
В классе Object .Equals реализует идентичность, а не равенство. Он проверяет, равны ли ссылки. Код может быть таким:
public virtual Boolean Equals(Object other) { if (this == other) return true; return false; }
При реализации .Equals в своем классе вы должны вызывать базовый класс .Equals, только если базовый класс не является Object. Ага, это сложно.
Более того, поскольку производные классы могут переопределять .Equals, и поэтому вы не можете вызывать его для проверки личности, Microsoft добавила статический метод .ReferenceEquals.
Если вы используете какой-то класс, то логически .Equals проверяет равенство, а .ReferenceEquals проверяет идентичность.
источник
Я расширил отличный ответ Ани, чтобы показать ключевые различия при работе со ссылочными типами и переопределенными методами равенства.
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 (i.e. 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 (i.e. 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("e.g. 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 //i.e. 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()
проверяет хэш-код или эквивалентность в зависимости от базового типа (значение / ссылка) иReferenceEquals()
предназначен для постоянной проверки хэш-кода.ReferenceEquals
возвращается,true
если оба объекта указывают на одну и ту же ячейку памяти.double e = 1.5; double d = e; object o1 = d; object o2 = d; Console.WriteLine(o1.Equals(o2)); // True Console.WriteLine(Object.Equals(o1, o2)); // True Console.WriteLine(Object.ReferenceEquals(o1, o2)); // False Console.WriteLine(e.Equals(d)); // True Console.WriteLine(Object.Equals(e, d)); // True Console.WriteLine(Object.ReferenceEquals(e, d)); // False
источник