2009-05-02 4 views
403

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

Вот код:

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

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

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

+2

См. Также: http://stackoverflow.com/questions/144530/or-equals – Arrow

+2

@ColonelPanic Silverlight классифицируется как «WPF везде», WPF построен на вершине .NET, и на вопрос используется C#, который является. NET. –

+4

Строка переопределяет '==', но операторы не являются полиморфными. В этом коде оператор '==' вызывается в типе 'object', который сопоставляет идентификатор вместо значения. –

ответ

339

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

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

+41

Если оператор специально не реализован в классе –

+15

@DominicCronin Это неправда. Даже если == реализован в классе, он будет проигнорирован, так как тип слева от сравнения является объектом. Похоже, что перегрузки операторов определяются во время компиляции, и во время компиляции все, что он знает, это то, что левая сторона является объектом. – MikeKulls

+0

@MikeKulls В вопросе тип, возвращаемый .Content, является объектом, поэтому да. Однако Мехрдад говорит «тип объекта», а не «ссылка на объект типа». Моя интерпретация заключалась в том, что он имел в виду ссылочный тип и что реализация == решила бы это на объекте. Тем не менее, перегрузки операторов разрешаются так же, как и для виртуальных методов. –

218

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

Обычно (если дело не со строками, то есть), Equals сравнивает значения, а == сравнивает ссылки на объекты. Если два объекта, которые вы сравниваете, ссылаются на один и тот же точный экземпляр объекта, то оба будут возвращать true, но если один имеет тот же контент и поступает из другого источника (это отдельный экземпляр с теми же данными), то только Equals вернет 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 
+6

Пятно на. Оператор '==' сравнивает ссылки на объекты (неглубокое сравнение), тогда как .Equals() сравнивает содержание объектов (глубокое сравнение). Как сказал @mehrdad, .Equals() переопределяется, чтобы обеспечить глубокое сравнение контента. – Andrew

+1

Я останусь здесь, потому что считаю, что важно подчеркнуть, что не происходит *, поскольку вы должны уделять пристальное внимание, чтобы понять это. (И я думаю, что код, демонстрирующий правильное и неправильное понимание, тоже стоит.) Надеюсь, что рейтинг не опустится ниже 0. – BlueMonkMN

+4

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

40

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

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

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

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

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

+0

Ну, Джаред, вы прямо нарушаете знаменитого Джеффа «Лучший код вообще не является кодом». Это действительно оправдано? С другой стороны, я могу видеть, откуда это происходит, и почему было бы желательно сделать семантику явной. В этом случае я очень предпочитаю, чтобы VB имел дело с равенством объектов. Он короткий * и * однозначный. –

+0

@ Konrad, я действительно должен был сказать «когда я незнаком с типом, я считаю, что лучшая практика такова». Да, VB имеет гораздо лучшую семантику здесь, потому что она действительно разделяет ценность и ссылочное равенство. C# смешивает два вместе и иногда вызывает ошибки неоднозначности. – JaredPar

+7

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

2

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

+2

У вас его обратно. Для начала Equals («Energy Attack») не прерывается, == это тот, который возвращает false. Ошибка ==, потому что она использует объект from, а не строку. – MikeKulls

+0

По умолчанию оператор == проверяет ссылочное равенство, определяя, указывают ли две ссылки на один и тот же объект. Поэтому ссылочные типы не должны реализовывать оператор ==, чтобы получить эту функциональность. Если тип неизменен, то есть данные, которые содержатся в экземпляре, не могут быть изменены, перегрузка оператора == для сравнения равенства значений вместо ссылочного равенства может быть полезна, поскольку в качестве неизменяемых объектов их можно считать такими же длинными поскольку они имеют одинаковую ценность. Не рекомендуется переопределять оператор == в неизменяемых типах. –

11

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

+0

+1, потому что это просто помогло мне понять, о чем идет речь! –

+1

Точно. @DominicCronin: всегда соблюдайте предупреждения времени компиляции. Если у вас есть объект expr = XXX; if (expr == «Энергия») {...} ', тогда, поскольку левая сторона имеет тип' object' типа компиляции, компилятор должен использовать перегрузку 'operator == (object, object)' , Он проверяет ссылочное равенство. Может ли быть трудно предсказать из-за [string interning] (http://en.wikipedia.org/wiki/String_interning). Если вы _know_, то левая сторона является либо «null», либо «string» типа, перед тем, как использовать '==', поместите левую сторону в 'string'. –

+0

, чтобы поместить часть этого другого пути. == (при определении того, использует ли он ссылочное равенство или равенство значения) зависит от типа времени компиляции/статического типа/левой стороны. (это тип, который разрешен при анализе времени компиляции). Вместо типа времени выполнения/динамического типа/типа RHS. Код BlueMonkMN показывает, что, хотя и не с кастингом. – barlop

1

Добавление еще одного пункта в ответ.

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

8

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

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

+2

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

13

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

> 2 == 2.0 
True 

> 2.Equals(2.0) 
False 

И для струнных

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

> x.Equals(null) 
NullReferenceException 

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

+0

Я не уверен, что я буду рассматривать принуждение интегральных типов к типам с плавающей запятой с оператором '==', чтобы быть хорошим. Например, должно ли 16777216.0f равно (int) 16777217, (double) 16777217.0, обоих или ни одного? Сравнение между интегральными типами является точным, но сравнение с плавающей запятой должно выполняться только ИМХО со значениями, которые явно передаются в соответствующие типы. Сравнение «float» с чем-то иным, чем «float», или «double» с чем-то другим, чем «double», поражает меня как основной запах кода, который не следует компилировать без диагностики. – supercat

+1

@supercat Я согласен - это огорчает, что 'x == y' не подразумевает' x/3 == y/3' (попробуйте 'x = 5' и' y = 5.0'). –

+0

Я считаю, что использование '/' для целочисленного деления является дефектом в дизайне C# и Java. Паскаль 'div' и даже VB.NET' '' намного лучше. Проблемы с '==' хуже, хотя: 'x == y' и' y == z' не подразумевают, что 'x == z' (рассмотрим три числа в моем предыдущем комментарии). Что касается отношения, которое вы предлагаете, даже если 'x' и' y' являются как 'float', так и' double', 'x.equals ((Object) y)' не означает, что '1.0f/x ==' 1.0f/y' (если бы у меня были мои барабанщики, это гарантировало бы это, даже если '==' не отличает положительный и нулевой, 'Equals' должен). – supercat

0

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

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

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

+0

Вместо того, чтобы иметь сопоставление int-to-float return false, я предпочитаю подход, который использует F #, что вовсе не должно исключать такое сравнение. Затем программист может решить, как и как обрабатывать тот факт, что значения имеют другой тип. Потому что иногда, в конце концов, мы * делаем * хотим рассматривать '3' как равные' 3.0f'. Если мы требуем, чтобы программист сказал, что предназначено в каждом случае, тогда нет опасности поведения по умолчанию, приводящего к непреднамеренным результатам, поскольку поведение по умолчанию отсутствует. – phoog

+0

@phoog: Мое личное чувство состоит в том, что языки должны иметь свои «нормальные» средства тестирования равенства, реализующие отношение эквивалентности и запрещающие все комбинации операндов, для которых это не было бы. Я не вижу огромного преимущества наличия равенства проверки языка между целыми числами и поплавками, подтверждая, что float точно представляет целое число, которое соответствует int, или просто запрещает такие сравнения, но будет рассматривать любой подход, превосходящий выполнение языка преобразование с потерями перед сравнением. – supercat

-1

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

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

+1

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

-2

==

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

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

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

Int А (00000011) == байт б (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 == с является истинным A == B является ложным

битовая комбинация являются одинаковыми для а и с, так что они равны, используя ==.

Равные():

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

Такие, как два разных объекта Строковые, что оба представляют символы в «Джейн»

+1

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

-2

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

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

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

См. here для получения более подробного объяснения.

+0

Object.Equals необязательно смотреть на поразрядное равенство. Это виртуальный метод, и переопределение может делать все, что захочет. – phoog

+0

Да, вы правы, вы можете делать все, что хотите, переопределить.но тема, о которой мы говорим, является реализацией по умолчанию. по умолчанию реализация Object.Equals является поразрядным равенством. –

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 
0

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

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

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

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

6

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

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

Надеюсь, я прав, и это ответ на ваш вопрос.

2

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

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

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

Справочная информация и summerize:

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

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

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

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

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

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

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

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

Параметры

a Строка

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

b Строка

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

Возвращает Boolean

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

Смежные вопросы