C # разница между == и Equals ()

434

У меня есть условие в приложении silverlight, которое сравнивает 2 строки, по какой-то причине, когда я использую ==, он возвращает false, а .Equals() возвращает true.

Вот код:

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
    // Execute code
}

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
    // Execute code
}

Любая причина, почему это происходит?

  • 2
    Смотрите также: stackoverflow.com/questions/144530/or-equals
  • 6
    Строка переопределяет == , но операторы не являются полиморфными. В этом коде оператор == вызывается для object типа, который выполняет сравнение идентификаторов вместо значения один.
Показать ещё 3 комментария
Теги:
equals

16 ответов

357
Лучший ответ

Когда == используется для выражения типа object, он будет решать System.Object.ReferenceEquals.

Equals является всего лишь методом virtual и ведет себя как таковой, поэтому будет использоваться переопределенная версия (которая для string сравнивает содержимое).

  • 49
    Если оператор специально не реализован в классе
  • 16
    @DominicCronin Это не правда. Даже если == реализован в классе, он будет проигнорирован, потому что тип слева от сравнения - объект. Похоже, что перегрузки операторов определяются во время компиляции, и во время компиляции все, что он знает, это то, что левая часть является объектом.
Показать ещё 12 комментариев
235

При сравнении ссылки объекта на строку (даже если ссылка на объект ссылается на строку), особое поведение оператора ==, специфичного для класса строки, игнорируется.

Обычно (при отсутствии строк со строками) Equals сравнивает значения, а == сравнивает ссылки на объекты. Если два объекта, которые вы сравниваете, ссылаются на один и тот же точный экземпляр объекта, то оба будут возвращать true, но если один имеет тот же контент и поступает из другого источника (это отдельный экземпляр с теми же данными), то только Equals будут return true. Однако, как отмечено в комментариях, строка является особым случаем, потому что она переопределяет оператор ==, так что при использовании чисто ссылок на строки (а не ссылок на объекты) сравниваются только значения, даже если они являются отдельными экземплярами. Следующий код иллюстрирует тонкие различия в поведении:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));

Вывод:

True True True
False True True
False False True
  • 7
    Пятно на. Оператор '==' сравнивает ссылки на объекты (поверхностное сравнение), тогда как .Equals () сравнивает содержимое объекта (глубокое сравнение). Как сказал @mehrdad, .Equals () переопределяется для обеспечения такого глубокого сравнения содержимого.
  • 1
    Я оставлю этот пост здесь, потому что считаю важным подчеркнуть то, что не происходит, так как вы должны уделять пристальное внимание, чтобы понять это. (И я думаю, что код для демонстрации правильного и неправильного понимания тоже стоит.) Я надеюсь, что рейтинг не опустится ниже 0.
Показать ещё 6 комментариев
39

== и .Equals зависят от поведения, определенного в фактическом типе и фактическом типе на сайте вызова. Оба являются просто методами/операторами, которые могут быть переопределены на любом типе и заданы любым поведением, которое автор так желает. По моему опыту, я нахожу общепринятым для людей в реализации .Equals для объекта, но пренебрегать внедрением оператора ==. Это означает, что .Equals будет фактически измерять равенство значений, а == будет измерять, являются ли они одной и той же ссылкой.

Когда я работаю с новым типом, определение которого в потоке или написание общих алгоритмов, я считаю, что наилучшая практика заключается в следующем

  • Если я хочу сравнить ссылки в С#, я использую Object.ReferenceEquals напрямую (не обязательно в общем случае)
  • Если я хочу сравнивать значения, я использую EqualityComparer<T>.Default

В некоторых случаях, когда я считаю, что использование == неоднозначно, я явно использую Object.Reference в коде, чтобы удалить неоднозначность.

Эрик Липперт недавно сделал сообщение в блоге по вопросу о том, почему в CLR есть два метода равенства. Стоит прочитать

  • 0
    Ну, Джаред, ты прямо нарушаешь знаменитый Джефф: «Лучший код - это вообще не код». Это действительно оправдано? С другой стороны, я могу видеть, откуда это происходит и почему было бы желательно сделать семантику явной. В этом случае я очень предпочитаю, чтобы VB работал с объектным равенством. Это коротко и однозначно.
  • 0
    @ Конрад, я действительно должен был сказать «когда я незнаком с типом, я считаю, что лучшая практика заключается в следующем». Да, здесь у VB гораздо лучшая семантика, потому что она действительно разделяет равенство значений и ссылок. C # смешивает их вместе, и это иногда вызывает ошибки неоднозначности.
Показать ещё 1 комментарий
16

Во-первых, есть разница. Для чисел

> 2 == 2.0
True

> 2.Equals(2.0)
False

И для строк

> string x = null;
> x == null
True

> x.Equals(null)
NullReferenceException

В обоих случаях == ведет себя более полезно, чем .Equals

  • 1
    Я не уверен, что считаю правильным приведение целочисленных типов к типам с плавающей точкой с помощью оператора == . Например, должен ли 16777216.0f быть равен (int) 16777217, (double) 16777217.0, обоим или ни одному? Сравнения между целочисленными типами хороши, но сравнения с плавающей точкой должны выполняться только IMHO со значениями, которые явно приводятся к сопоставляемым типам. Сравнение числа с float с чем-то отличным от числа с float или double с чем-то отличным от double , кажется мне основным запахом кода, который не должен компилироваться без диагностики.
  • 1
    @supercat Я согласен - печально, что x == y не подразумевает x/3 == y/3 (попробуйте x = 5 и y = 5.0 ).
Показать ещё 2 комментария
12

Я бы добавил, что если вы нанесете свой объект на строку, то он будет работать правильно. Вот почему компилятор даст вам предупреждение:

Возможное непреднамеренное сравнение ссылок; чтобы получить сравнение значений, отбросьте левую сторону, чтобы ввести 'string'

  • 1
    Именно так. @DominicCronin: всегда соблюдайте предупреждения во время компиляции. Если у вас есть object expr = XXX; if (expr == "Energy") { ... } , то, поскольку левая часть имеет object типа времени компиляции, компилятор должен использовать operator ==(object, object) перегрузки operator ==(object, object) . Проверяет равенство ссылок. Будет ли это давать true или false трудно предсказать из-за интернирования строк . Если вы знаете, что левая сторона имеет null или имеет тип string , приведите левую сторону к string перед использованием == .
  • 0
    поставить часть этого по-другому. == (при определении, использует ли он ссылочное равенство или равенство значений), зависит от типа времени компиляции / статического типа / типа левой стороны. (это тип, который разрешается в анализе времени компиляции). Вместо типа времени выполнения / динамического типа / типа RHS. Код BlueMonkMN показывает это, хотя и не с кастингом.
8

Насколько я понимаю, ответ прост:

  • == сравнивает ссылки на объекты.
  • .Equals сравнивает содержимое объекта.
  • String datatypes всегда действуют как сравнение содержимого.

Я надеюсь, что я прав, и что он ответил на ваш вопрос.

8

== Оператор 1. Если операнды Типы значений и их значения равны, он возвращает true else false. 2. Если операнды Reference Types, за исключением строки, и оба относятся к одному и тому же объекту, он возвращает true else false. 3. Если операнды являются строковыми типами и их значения равны, он возвращает true else false.

.equals 1. Если операнды являются ссылочными типами, он выполняет Reference Equality, если они относятся к одному и тому же объекту, он возвращает true else false. 2. Если Операнды являются типами значений, то в отличие от оператора ==, он сначала проверяет их тип, и если их типы одинаковы, он выполняет == operator else, он возвращает false.

  • 2
    Это не правильно. Оператор == может быть перегружен для любого типа, а не только для строки. Описание исключительного случая только для строки искажает семантику оператора. Было бы более точным, хотя, возможно, и не очень полезным, сказать: «если операнды являются ссылочными типами, он возвращает истину, если операнды ссылаются на один и тот же объект, если нет соответствующей перегрузки, и в этом случае реализация этой перегрузки определяет результат ». То же самое верно для Equals с дополнительным усложнением, что это виртуальный метод, поэтому его поведение может быть переопределено, а также перегружено.
2

Поскольку статическая версия метода .Equal не была упомянута до сих пор, я хотел бы добавить это здесь, чтобы обобщить и сравнить 3 варианта.

MyString.Equals("Somestring"))          //Method 1
MyString == "Somestring"                //Method 2
String.Equals("Somestring", MyString);  //Method 3 (static String.Equals method) - better

где MyString - это переменная, которая поступает из другого места в коде.

Фоновая информация и лето:

В Java, использующем == для сравнения строк, не следует использовать. Я упоминаю об этом, если вам нужно использовать оба языка, а также сообщить, что использование == также может быть заменено чем-то лучше на С#.

В С# нет никакой практической разницы для сравнения строк с использованием метода 1 или метода 2, если оба имеют строку типа. Однако, если один из них является нулевым, один имеет другой тип (например, целое число) или один представляет объект, который имеет другую ссылку, то, как показывает первоначальный вопрос, может возникнуть впечатление, что сравнение содержимого для равенства может не вернуть вы ожидаете.

Предлагаемое решение:

Поскольку использование == не совсем то же самое, что использовать .Equals при сравнении вещей, вы можете вместо этого использовать статический метод String.Equals. Таким образом, если обе стороны не являются одним и тем же типом, вы по-прежнему будете сравнивать контент, а если он равен нулю, вы избежите исключения.

   bool areEqual = String.Equals("Somestring", MyString);  

Это немного больше, чтобы писать, но, на мой взгляд, безопаснее использовать.

Вот некоторая информация, скопированная с Microsoft:

public static bool Equals (string a, string b);

параметры

строка

Первая строка для сравнения или null.

b Строка

Вторая строка для сравнения или null.

Возвращает Boolean

true если значение a совпадает с значением b; в противном случае - false. Если оба значения a и b равны null, метод возвращает true.

2

Существует еще одно измерение более раннего ответа от @BlueMonkMN. Дополнительное измерение состоит в том, что ответ на вопрос о названии @Drahcir, как указано, также зависит от того, как мы достигли значения string. Чтобы проиллюстрировать:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
string s5 = "te" + "st";
object s6 = s5;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));

Console.WriteLine("\n  Case1 - A method changes the value:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));

Console.WriteLine("\n  Case2 - Having only literals allows to arrive at a literal:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s5), s1 == s5, s1.Equals(s5));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s6), s1 == s6, s1.Equals(s6));

Вывод:

True True True

  Case1 - A method changes the value:
False True True
False False True

  Case2 - Having only literals allows to arrive at a literal:
True True True
True True True
2

Добавление еще одной точки в ответ.

.EqualsTo() дает вам возможность сравнивать с культурой и чувствительностью к регистру.

2

Я немного смущен. Если тип содержимого времени выполнения имеет строку типа, то оба == и Equals должны возвращать значение true. Однако, поскольку это не так, тогда тип времени выполнения не является строкой, а вызов Equals на нем выполняет ссылочное равенство, и это объясняет, почему Equals ( "Energy Attack" ) терпит неудачу. Однако во втором случае решение о том, какой перегруженный == статический оператор следует вызывать, выполняется во время компиляции, и это решение выглядит как (строка, строка). это говорит мне, что Content обеспечивает неявное преобразование в строку.

  • 2
    У вас есть это обратно на фронт. Для начала Equals («Энергетическая атака») не терпит неудачу, == тот, который возвращает false Сбой ==, потому что он использует == из объекта, а не строки.
  • 0
    По умолчанию оператор == проверяет равенство ссылок, определяя, указывают ли две ссылки на один и тот же объект. Следовательно, ссылочные типы не должны реализовывать оператор == для получения этой функциональности. Когда тип является неизменяемым, то есть данные, содержащиеся в экземпляре, не могут быть изменены, может быть полезен оператор перегрузки == для сравнения равенства значений вместо ссылочного равенства, поскольку в качестве неизменяемых объектов их можно считать одинаковыми как long так как они имеют одинаковое значение. Не стоит переопределять оператор == в неизменяемых типах.
0

Действительно отличные ответы и примеры!

Я просто хотел бы добавить принципиальное различие между ними,

Операторы, такие как ==, не являются полиморфными, а Equals -

С учетом этой концепции, если вы выработаете какой-либо пример (посмотрев на ссылочный тип левой руки и правой руки и проверив/зная, действительно ли тип перегружен оператором ==, а Equals переопределены), вы наверняка получите правильный ответ.

0

Ток == в С# используется для двух разных операторов проверки равенства. Когда компилятор встречает этот токен, он проверяет, осуществил ли какой-либо из сравниваемых типов перегрузку оператора равенства или для сравнения отдельных типов комбинации (*), или для комбинации типов, к которым могут быть преобразованы оба типа. Если компилятор найдет такую ​​перегрузку, он будет использовать его. В противном случае, если два типа являются ссылочными типами, и они не являются несвязанными классами (либо они могут быть интерфейсом, либо могут быть связанными классами), компилятор будет рассматривать == как оператор сравнения ссылок. Если ни одно условие не применяется, компиляция завершится неудачно.

Обратите внимание, что некоторые другие языки используют отдельные токены для двух операторов проверки равенства. Например, в VB.NET токен = используется в выражениях исключительно для перегружаемого оператора проверки равенства, а Is используется как оператор ссылочного теста или нулевого теста. An использовать = для типа, который не переопределяет оператор проверки равенства, не будет работать, как будет пытаться использовать Is для любых целей, кроме проверки ссылочного равенства или недействительности.

(*) Типы обычно только перегружают равенство для сравнения с самим собой, но может быть полезно, чтобы типы перегружали оператор равенства для сравнения с другими конкретными типами; например, int мог бы (и IMHO должен был иметь, но не сделал) определил операторы равенства для сравнения с float, так что 16777217 не сообщил бы себя равным 16777216f. Как бы то ни было, поскольку такой оператор не определен, С# будет продвигать int до float, округляя его до 16777216f, прежде чем оператор проверки равенства увидит его; этот оператор затем видит два равных числа с плавающей запятой и сообщает о них как о равных, не подозревая о округлении, которое имело место.

  • 0
    Вместо того, чтобы сравнение int-to-float возвращало false, я предпочитаю подход, который использует F #, который вообще запрещает такое сравнение. Затем программист может решить, следует ли и как обрабатывать тот факт, что значения имеют различный тип. Потому что иногда, в конце концов, мы хотим рассматривать 3 как равное 3.0f . Если мы требуем, чтобы программист говорил, что предназначено в каждом случае, тогда нет опасности поведения по умолчанию, которое приведет к непредвиденным результатам, так как поведение по умолчанию отсутствует.
  • 0
    @phoog: Мое личное мнение состоит в том, что языки должны иметь свои «нормальные» средства тестирования на равенство, реализующие отношение эквивалентности и запрещающие любые комбинации операндов, для которых это не так. Я не вижу огромного преимущества наличия проверки языка на равенство между целыми числами и числами с плавающей точкой, подтверждая, что число с плавающей точкой точно представляет целое число, совпадающее с целым числом, по сравнению с простым запрещением таких сравнений, но я бы посчитал, что любой подход превосходит выполнение языка преобразование с потерями перед сравнением.
-2

Единственное различие между Equal и == заключается в сравнении типов объектов. в других случаях, таких как ссылочные типы и типы значений, они почти одинаковы (либо оба являются поразрядным равенством, либо оба являются ссылочным равенством).

объект: Равновесие: побитовое равенство ==: ссылочное равенство

string: (equals и == одинаковы для строки, но если одна из строк изменилась на объект, результат сравнения будет другим) Равновесие: побитовое равенство ==: поэтапное равенство

Подробнее см. здесь.

  • 0
    Object.Equals не обязательно смотрит на битовое равенство. Это виртуальный метод, и переопределение может делать все, что захочет.
  • 0
    да, вы правы, вы можете сделать все, что хотите, чтобы переопределить это. но тема, о которой мы говорим, является реализацией по умолчанию. реализация Object.Equals по умолчанию - побитовое равенство.
-2

Когда мы создаем какой-либо объект, в объекте есть две части, одна из которых является контентом, а другая - ссылкой на этот контент. == сравнивает как контент, так и ссылку; equals() сравнивает только контент

http://www.codeproject.com/Articles/584128/What-is-the-difference-between-equalsequals-and-Eq

  • 1
    Это неправда. Если a и b оба являются строковыми ссылками, то результат a == b не зависит от того, указывают ли ссылки на один и тот же объект.
-3

==

Оператор == может использоваться для сравнения двух переменных любого типа, а просто сравнивает бит.

int a = 3;
byte b = 3;
if (a == b) { // true }

Примечание: в левой части int больше нулей, но здесь нас это не волнует.

int a (00000011) == byte b (00000011)

Помните == оператор заботится только о шаблоне бит в переменной.

Использовать == Если две ссылки (примитивы) относятся к одному и тому же объекту в куче.

Правила одинаковы, является ли переменная ссылкой или примитивом.

Foo a = new Foo();
Foo b = new Foo();
Foo c = a;

if (a == b) { // false }
if (a == c) { // true }
if (b == c) { // false }

a == c истинно a == b является ложным

бит-шаблон одинаковый для a и c, поэтому они равны с использованием ==.

Равно():

Используйте метод equals(), чтобы увидеть , если два разных объекта равны.

Например, два разных объекта String, которые оба представляют символы в "Jane"

  • 1
    Это неверно Рассмотрим следующее: object a = 3; object b = 3; Console.WriteLine(a == b); , Вывод ложен, хотя битовые комбинации значений одинаковы. Типы операндов также имеют значение. Причина, по которой мы «не заботимся» о различном количестве нулей в вашем примере, заключается в том, что к тому моменту, когда мы вызываем оператор равенства, число нулей фактически одинаково из-за неявного преобразования.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню