Страницы

Поиск по вопросам

воскресенье, 1 декабря 2019 г.

Как в C# правильно сравнивать строки

#c_sharp #c_sharp_faq


Как в C# правильно сравнивать строки: Equals или ==?

string str1 = "s";
string str2 = "s";

Console.WriteLine("eq: " + str1.Equals(str2));
Console.WriteLine("==: " + (str1 == str2));


В обоих случаях результат True, хотя String является классом и оператор == должен
был сравнить ссылки.

IlDasm показал, что создаются 2 переменные и сравниваются согласно методам Equals
и == (op_Equality)

  IL_0000:  nop
  IL_0001:  ldstr      "s"
  IL_0006:  stloc.0
  IL_0007:  ldstr      "s"
  IL_000c:  stloc.1
  IL_000d:  ldstr      "eq: "
  IL_0012:  ldloc.0
  IL_0013:  ldloc.1
  IL_0014:  callvirt   instance bool [mscorlib]System.String::Equals(string)
  IL_0019:  box        [mscorlib]System.Boolean
  IL_001e:  call       string [mscorlib]System.String::Concat(object, object)
  IL_0023:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0028:  nop
  IL_0029:  ldstr      "==: "
  IL_002e:  ldloc.0
  IL_002f:  ldloc.1
  IL_0030:  call       bool [mscorlib]System.String::op_Equality(string, string)
  IL_0035:  box        [mscorlib]System.Boolean
  IL_003a:  call       string [mscorlib]System.String::Concat(object, object)

    


Ответы

Ответ 1



В C# правильно сравнивать строки и через ==, и через Equals. Но более предпочтительным будет сравнивать через ==. Почему так? Метод Equals подразумевает сравнение значений объектов ссылочного типа, он объявлен как virtual и для строк он перегружен и сравнивает их, как и предполагается, по значению. В Ваших классах Вы должны давать свою реализацию для него. Иначе он будет вести себя как ReferenceEquals и для ссылок, которые указывают не на один объект будет давать false, хоть они и будут равны по значению. Оператор == для строк представляют свою реализацию, отличную от стандартной для всех других объектов ссылочного типа. Если сравниваемые ссылки имеют тип System.String, то он сначала сравнит указывают ли ссылки на один тот же объект и если нет, то будет сравнивать две ссылки типа System.String по значению. Но маленькое замечание, если мы сравниваем объект типа System.String (string) с объектом типа System.Object (object), который указывает на строку, они будут сравниваться по значениям. Пример: string str = "str"; object obj = "str"; bool result = str.Equals(obj); result будет равен true, так как obj приведётся к типу string, потому что метод Equals мы вызвали у строки (объекта типа string). Но если мы сравниваем объект типа System.Object (object), который указывает на строку, с объектом типа System.String (string) они будут сравниваться по ссылке. Пример: string str = "str"; object obj = "str"; bool result = obj.Equals(str); result будет равен false, так как str приведётся к типу object, потому что метод Equals мы вызвали у объекта типа object. Поэтому в данном случае важно привести object к типу string

Ответ 2



Оператор == не обязан сравнивать только ссылки. В C# операторы можно переопределять, и он, естественно, переопределен для множества типов. Сравнение ссылок - это просто поведение по умолчанию для типов, для которых оператор == не переопределен. Для типов, для которых переопределен оператор ==, скорее всего будет переопределен и Equals, причем результат для обоих способов сравнения скорее всего будет совпадать (если не будет - значит при проектировании типа его автор просто ошибся). Вот реализация == для String из reference source: public static bool operator == (String a, String b) { return String.Equals(a, b); } public static bool Equals(String a, String b) { if ((Object)a==(Object)b) { return true; } if ((Object)a==null || (Object)b==null) { return false; } if (a.Length != b.Length) return false; return EqualsHelper(a, b); } А вот реализация нестатического Equals оттуда же: public override bool Equals(Object obj) { if (this == null) //this is necessary to guard against reverse-pinvokes and throw new NullReferenceException(); //other callers who do not use the callvirt instruction String str = obj as String; if (str == null) return false; if (Object.ReferenceEquals(this, obj)) return true; if (this.Length != str.Length) return false; return EqualsHelper(this, str); } Они отличаются только небольшой поправкой на нестатичность. Обе сравнивают ссылки и проверяют случай null != null, потом сравнивают длину на неравенство, и только потом - значение.

Ответ 3



В C# для строк перегружен и оператор == и метод Equals, так как строки чаще требуется сравнивать по значению, нежели по ссылкам. В случае с оператором == сначала происходит сравнение указателей строк, и если они равны, то метод возвращает true. В противном случае проверяется равенство операндов null (если хотя бы один из них равен null, возвращается false). Затем сравниваются длины строк. А затем происходит посимвольное сравнение строк (то есть сравнение по значению). Это делается с использованием указателей в неуправляемом коде (очевидно из соображений производительности), а также применяется размотка циклов (очевидно из тех же соображений, но это уже малоинтересные в данном случае детали). Если интересны детали, посмотреть можете здесь

Комментариев нет:

Отправить комментарий