У меня есть условие в приложении silverlight, которое сравнивает 2 строки, по какой-то причине, когда я использую ==
, он возвращает false, а .Equals()
возвращает true.
Вот код:
if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
// Execute code
}
if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
// Execute code
}
Любая причина, почему это происходит?
Когда ==
используется для выражения типа object
, он будет решать System.Object.ReferenceEquals
.
Equals
является всего лишь методом virtual
и ведет себя как таковой, поэтому будет использоваться переопределенная версия (которая для string
сравнивает содержимое).
При сравнении ссылки объекта на строку (даже если ссылка на объект ссылается на строку), особое поведение оператора ==
, специфичного для класса строки, игнорируется.
Обычно (при отсутствии строк со строками) 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
==
и .Equals
зависят от поведения, определенного в фактическом типе и фактическом типе на сайте вызова. Оба являются просто методами/операторами, которые могут быть переопределены на любом типе и заданы любым поведением, которое автор так желает. По моему опыту, я нахожу общепринятым для людей в реализации .Equals
для объекта, но пренебрегать внедрением оператора ==
. Это означает, что .Equals
будет фактически измерять равенство значений, а ==
будет измерять, являются ли они одной и той же ссылкой.
Когда я работаю с новым типом, определение которого в потоке или написание общих алгоритмов, я считаю, что наилучшая практика заключается в следующем
Object.ReferenceEquals
напрямую (не обязательно в общем случае)EqualityComparer<T>.Default
В некоторых случаях, когда я считаю, что использование ==
неоднозначно, я явно использую Object.Reference
в коде, чтобы удалить неоднозначность.
Эрик Липперт недавно сделал сообщение в блоге по вопросу о том, почему в CLR есть два метода равенства. Стоит прочитать
Во-первых, есть разница. Для чисел
> 2 == 2.0
True
> 2.Equals(2.0)
False
И для строк
> string x = null;
> x == null
True
> x.Equals(null)
NullReferenceException
В обоих случаях ==
ведет себя более полезно, чем .Equals
==
. Например, должен ли 16777216.0f быть равен (int) 16777217, (double) 16777217.0, обоим или ни одному? Сравнения между целочисленными типами хороши, но сравнения с плавающей точкой должны выполняться только IMHO со значениями, которые явно приводятся к сопоставляемым типам. Сравнение числа с float
с чем-то отличным от числа с float
или double
с чем-то отличным от double
, кажется мне основным запахом кода, который не должен компилироваться без диагностики.
x == y
не подразумевает x/3 == y/3
(попробуйте x = 5
и y = 5.0
).
Я бы добавил, что если вы нанесете свой объект на строку, то он будет работать правильно. Вот почему компилятор даст вам предупреждение:
Возможное непреднамеренное сравнение ссылок; чтобы получить сравнение значений, отбросьте левую сторону, чтобы ввести 'string'
object expr = XXX; if (expr == "Energy") { ... }
, то, поскольку левая часть имеет object
типа времени компиляции, компилятор должен использовать operator ==(object, object)
перегрузки operator ==(object, object)
. Проверяет равенство ссылок. Будет ли это давать true
или false
трудно предсказать из-за интернирования строк . Если вы знаете, что левая сторона имеет null
или имеет тип string
, приведите левую сторону к string
перед использованием ==
.
Насколько я понимаю, ответ прост:
Я надеюсь, что я прав, и что он ответил на ваш вопрос.
== Оператор 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.
==
может быть перегружен для любого типа, а не только для строки. Описание исключительного случая только для строки искажает семантику оператора. Было бы более точным, хотя, возможно, и не очень полезным, сказать: «если операнды являются ссылочными типами, он возвращает истину, если операнды ссылаются на один и тот же объект, если нет соответствующей перегрузки, и в этом случае реализация этой перегрузки определяет результат ». То же самое верно для Equals
с дополнительным усложнением, что это виртуальный метод, поэтому его поведение может быть переопределено, а также перегружено.
Поскольку статическая версия метода .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
.
Существует еще одно измерение более раннего ответа от @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
Добавление еще одной точки в ответ.
.EqualsTo()
дает вам возможность сравнивать с культурой и чувствительностью к регистру.
Я немного смущен. Если тип содержимого времени выполнения имеет строку типа, то оба == и Equals должны возвращать значение true. Однако, поскольку это не так, тогда тип времени выполнения не является строкой, а вызов Equals на нем выполняет ссылочное равенство, и это объясняет, почему Equals ( "Energy Attack" ) терпит неудачу. Однако во втором случае решение о том, какой перегруженный == статический оператор следует вызывать, выполняется во время компиляции, и это решение выглядит как (строка, строка). это говорит мне, что Content обеспечивает неявное преобразование в строку.
Действительно отличные ответы и примеры!
Я просто хотел бы добавить принципиальное различие между ними,
Операторы, такие как
==
, не являются полиморфными, аEquals
-
С учетом этой концепции, если вы выработаете какой-либо пример (посмотрев на ссылочный тип левой руки и правой руки и проверив/зная, действительно ли тип перегружен оператором ==, а Equals переопределены), вы наверняка получите правильный ответ.
Ток ==
в С# используется для двух разных операторов проверки равенства. Когда компилятор встречает этот токен, он проверяет, осуществил ли какой-либо из сравниваемых типов перегрузку оператора равенства или для сравнения отдельных типов комбинации (*), или для комбинации типов, к которым могут быть преобразованы оба типа. Если компилятор найдет такую перегрузку, он будет использовать его. В противном случае, если два типа являются ссылочными типами, и они не являются несвязанными классами (либо они могут быть интерфейсом, либо могут быть связанными классами), компилятор будет рассматривать ==
как оператор сравнения ссылок. Если ни одно условие не применяется, компиляция завершится неудачно.
Обратите внимание, что некоторые другие языки используют отдельные токены для двух операторов проверки равенства. Например, в VB.NET токен =
используется в выражениях исключительно для перегружаемого оператора проверки равенства, а Is
используется как оператор ссылочного теста или нулевого теста. An использовать =
для типа, который не переопределяет оператор проверки равенства, не будет работать, как будет пытаться использовать Is
для любых целей, кроме проверки ссылочного равенства или недействительности.
(*) Типы обычно только перегружают равенство для сравнения с самим собой, но может быть полезно, чтобы типы перегружали оператор равенства для сравнения с другими конкретными типами; например, int
мог бы (и IMHO должен был иметь, но не сделал) определил операторы равенства для сравнения с float
, так что 16777217 не сообщил бы себя равным 16777216f. Как бы то ни было, поскольку такой оператор не определен, С# будет продвигать int
до float
, округляя его до 16777216f, прежде чем оператор проверки равенства увидит его; этот оператор затем видит два равных числа с плавающей запятой и сообщает о них как о равных, не подозревая о округлении, которое имело место.
3
как равное 3.0f
. Если мы требуем, чтобы программист говорил, что предназначено в каждом случае, тогда нет опасности поведения по умолчанию, которое приведет к непредвиденным результатам, так как поведение по умолчанию отсутствует.
Единственное различие между Equal и == заключается в сравнении типов объектов. в других случаях, таких как ссылочные типы и типы значений, они почти одинаковы (либо оба являются поразрядным равенством, либо оба являются ссылочным равенством).
объект: Равновесие: побитовое равенство ==: ссылочное равенство
string: (equals и == одинаковы для строки, но если одна из строк изменилась на объект, результат сравнения будет другим) Равновесие: побитовое равенство ==: поэтапное равенство
Подробнее см. здесь.
Когда мы создаем какой-либо объект, в объекте есть две части, одна из которых является контентом, а другая - ссылкой на этот контент.
==
сравнивает как контент, так и ссылку;
equals()
сравнивает только контент
http://www.codeproject.com/Articles/584128/What-is-the-difference-between-equalsequals-and-Eq
a
и b
оба являются строковыми ссылками, то результат a == b
не зависит от того, указывают ли ссылки на один и тот же объект.
==
Оператор == может использоваться для сравнения двух переменных любого типа, а просто сравнивает бит.
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"
object a = 3; object b = 3; Console.WriteLine(a == b);
, Вывод ложен, хотя битовые комбинации значений одинаковы. Типы операндов также имеют значение. Причина, по которой мы «не заботимся» о различном количестве нулей в вашем примере, заключается в том, что к тому моменту, когда мы вызываем оператор равенства, число нулей фактически одинаково из-за неявного преобразования.
==
, но операторы не являются полиморфными. В этом коде оператор==
вызывается дляobject
типа, который выполняет сравнение идентификаторов вместо значения один.