Мое понимание этих трех было:
-
.Equals()
тесты для равенства данных (из-за отсутствия лучшего описания). .Equals()
может возвращать True для разных экземпляров одного и того же объекта, и это наиболее часто переопределяемый метод.
-
.ReferenceEquals()
проверяет, являются ли два объекта одним и тем же экземпляром и не могут быть переопределены.
-
==
по умолчанию совпадает с ReferenceEquals()
, но этот CAN может быть переопределен.
Но Станция С# сообщает:
В классе объектов Equals
и Методы ReferenceEquals
семантически эквивалентный, за исключением того, что ReferenceEquals
работает только на объектов. ReferenceEquals
метод статический.
Теперь я не понимаю. Может ли кто-нибудь пролить свет на это?
Ответ 1
Источник вашей путаницы заключается в том, что в извлечении из станции С# есть опечатка, которая должна гласить: "... за исключением того, что Equals работает только на экземплярах объекта. Метод ReferenceEquals является статическим".
Вы слабо правы в отношении различий в семантическом значении каждого (хотя "разные экземпляры одного и того же объекта" кажутся немного запутанными, вероятно, следует прочитать "разные экземпляры одного и того же типа") и о том, какие из них могут быть переопределены.
Если мы оставим в стороне, пусть дело с последним битом вашего вопроса, то есть, как они работают с простыми System.Object
экземпляров и System.Object
ссылки (нам нужно как увернуться не-полиморфный характер ==
). Здесь все три операции будут работать одинаково, но с оговоркой: Equals
не могут быть вызваны при null
.
Equals
- это метод экземпляра, который принимает один параметр (который может быть null
). Поскольку это метод экземпляра (должен вызываться для реального объекта), он не может быть вызван для null
-reference.
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
Ответ 2
Посмотрите эту статью MSDN по этому вопросу.
Я думаю, что соответствующие моменты:
Чтобы проверить ссылочное равенство, используйте ReferenceEquals. Чтобы проверить равенство значений, используйте Equals или Equals.
По умолчанию оператор == проверяет ссылочное равенство, определяя, указывают ли две ссылки на один и тот же объект, поэтому для получения этой функциональности ссылочным типам не требуется реализовать operator ==. Когда тип является неизменным, то есть данные, содержащиеся в экземпляре, не могут быть изменены, перегрузка оператора == для сравнения равенства значений вместо ссылочного равенства может быть полезна, поскольку в качестве неизменяемых объектов их можно считать одинаковыми, если они имеют то же значение.
Надеюсь, это поможет!
Ответ 3
Ваше понимание .ReferenceEquals верное.
.Equals проверяет равенство данных для типов значений и ссылочное равенство для неценных типов (общие объекты).
. Элементы могут быть переопределены для объектов, чтобы выполнить некоторую проверку равенства данных
EDIT: Кроме того,.ReferenceEquals нельзя использовать для типов значений (ну, может, но всегда будет false)
Ответ 4
Хотите добавить мои пять центов о сравнении с "null".
-
ReferenceEquals (объект, объект) - это то же самое, что и "(object) arg1 == arg2" (поэтому в случае типов значений вы получаете бокс и требуется время). Но этот метод является единственным 100% безопасным способом проверки вашего аргумента для null в нескольких ситуациях, например
- a) перед вызовом его членов через. оператор
- b) проверка результата оператора AS.
-
== и Equals(). Почему я говорю, что ReferenceEquals на 100% безопасен с нулевыми проверками? Представьте, что вы пишете общие расширения в основных кросс-проектах libs и можете сказать, что вы ограничиваете общий тип параметра некоторым типом домена. Этот тип может ввести оператор "==" - теперь или позже (и поверьте, я много видел, этот оператор может иметь очень "странную" логику, особенно если речь идет о объектах домена или персистентности). Вы пытаетесь проверить свой аргумент на значение null, а затем называть его членом. Удивите, у вас здесь есть NullRef. Поскольку оператор == почти такой же, как Equals() - очень обыденный и очень непредсказуемый. Однако есть разница, которая должна быть принята во внимание - если вы не ограничиваете свой общий параметр каким-либо настраиваемым типом (== может использоваться только в том случае, если ваш тип является "классом" ), оператор == тот же, что и объект .ReferenceEquals(..). Реализация Equals всегда используется из конечного типа, поскольку она виртуальна.
Итак, моя рекомендация заключается в том, что когда вы пишете свои собственные типы или извлекаете из известных типов, вы можете использовать == для проверки нулевого значения. В противном случае используйте object.ReferenceEquals(arg, null).
Ответ 5
В классе объектов .Equals реализует тождество, а не равенство. Он проверяет, равны ли ссылки. Код может быть таким:
public virtual Boolean Equals(Object other) {
if (this == other) return true;
return false;
}
При реализации .Equals в вашем классе вы должны вызвать базовый класс .Equals, только если базовый класс не является Object. Да, это сложно.
Более того, поскольку производные классы могут переопределять .Equals и поэтому вы не можете вызвать его для проверки подлинности Microsoft добавила статический метод .ReferenceEquals.
Если вы используете какой-то класс, то для вас логически .Equals проверяет на равенство и .ReferenceEquals проверяет личность.
Ответ 6
Я расширил отличный ответ Ани, чтобы показать основные различия при работе со ссылочными типами и переопределенными методами равенства.
.
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 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 equality operator though, in which case we define what 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 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 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);
}
}
Ответ 7
Equals()
проверяет хэш-код или эквивалентность в зависимости от базового типа (Value/Reference) и 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