С.#Равняется.,)(ReferenceEquals () и = = оператор
мое понимание этих трех был:
.Equals()
тесты на равенство данных (из-за отсутствия лучшего описания)..Equals()
может возвращать True для разных экземпляров одного и того же объекта, и это наиболее часто переопределяемый метод..ReferenceEquals()
проверяет, являются ли два объекта одним экземпляром и не могут быть переопределены.==
такой жеReferenceEquals()
по умолчанию, но это может быть отвергнутым.
но станция C# гласит:
в классе объектов
Equals
иReferenceEquals
методы семантически эквивалентно, за исключением того, что theReferenceEquals
работает только на экземпляр объекта. ЭтотReferenceEquals
метод является статическим.
теперь я не понял. Кто-нибудь может пролить свет на это?
7 ответов
источником вашего замешательства, по-видимому, является то, что в выписке из станции C# есть опечатка, которая должна гласить: "... за исключением того, что равна работает только на экземпляры объектов. Метод ReferenceEquals является статическим."
вы слабо правы о различиях в семантических значениях каждого (хотя "разные экземпляры одного и того же объекта" кажется немного запутанным, он, вероятно, должен читать "разные экземпляры одного и того же тип) и о котором можно переопределить.
если мы оставим это в стороне, давайте рассмотрим последний бит вашего вопроса, то есть как они работают с plainSystem.Object
экземпляров и System.Object
ссылки (нужны обе, чтобы увернуться от не-полиморфный характер ==
). Здесь все три операции будут работать equivalentally, но с оговоркой:Equals
невозможно вызвать наnull
.
Equals
является методом экземпляра, который принимает один параметр (который can benull
). Поскольку это метод экземпляра (должен быть вызван на фактическом объекте), он не может быть вызван на 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.
по умолчанию оператор == проверяет равенство ссылок, определяя, указывают ли две ссылки на один и тот же объект, поэтому ссылочным типам не нужно реализовывать operator==, чтобы получить эту функциональность. Когда тип является неизменяемым, то есть данные, содержащиеся в экземпляре, не могут быть изменены, перегрузка operator == для сравнения равенства значений вместо ссылочного равенства может быть полезна, потому что, как неизменяемые объекты, они могут считаться одинаковыми, пока они имеют одинаковое значение.
надеюсь, что это помогает!
ваше понимание .ReferenceEquals является правильным.
.Equals проверяет равенство данных для типов значений и ссылочное равенство для типов без значений (общие объекты).
.Equals можно переопределить для объектов, чтобы выполнить некоторую форму проверки равенства данных
EDIT: также .ReferenceEquals нельзя использовать для типов значений (ну, это может быть, но всегда будет false)
хотите добавить мои пять центов о сравнении с"null".
-
ReferenceEquals(object, object)совпадает с "(object) arg1 == arg2" (поэтому в случае типов значений вы получаете бокс, и это занимает время). Но этот метод является единственным 100% безопасным способом проверки Вашего аргумента на null в нескольких ситуациях, таких как
- a) перед вызовом его членов через . оператор
- b) проверка результата AS оператор.
== и equals(). Почему я говорю, что ReferenceEquals на 100% безопасны с нулевыми проверками? Представьте, что вы пишете универсальные расширения в основных межпроектных библиотеках и ограничиваете тип универсального параметра некоторым типом домена. Этот тип может ввести оператор "= = " - сейчас или позже (и поверьте, я много видел, у этого оператора может быть очень "странная" логика, особенно если речь идет об объектах домена или постоянства). Вы попробуйте проверьте аргумент для null, а затем вызовите операцию члена на нем. Сюрприз, Вы можете получить NullRef здесь. Потому что = = оператор почти такой же, как Equals() - очень пользовательский и очень непредсказуемый. Однако есть разница, которую следует учитывать - если вы не ограничиваете свой общий параметр каким-либо пользовательским типом (== может использоваться только если ваш тип "класс"), = = оператор совпадает с объектом.Метод referenceequals(..). Реализация Equals всегда используется из final type, так как она виртуальный.
поэтому я рекомендую, когда вы пишете свои собственные типы или производные от известных типов, вы можете использовать == для проверки null. В противном случае используйте object.ReferenceEquals (arg, null).
в классе Object .Равно реализует тождество, а не равенство. Он проверяет, равны ли ссылки. Код может быть таким:
public virtual Boolean Equals(Object other) {
if (this == other) return true;
return false;
}
при осуществлении .Равных в своем классе вы должны вызвать базовый класс .Равно, только если базовый класс не является Object. Да, это сложно.
еще больше, так как производные классы могут переопределять .Равно и поэтому вы не можете вызвать его, чтобы проверить идентичность Microsoft добавила статику .Метод ReferenceEquals.
Если вы используете какой-то класс тогда для вас логически .Equals проверяет равенство и .ReferenceEquals проверяет личность.
я расширил на отличный ответ Ани чтобы показать ключевые различия при работе со ссылочными типами и переопределенными методами равенства.
- вы можете увидеть рабочую версию этот код: 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 (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