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

1011
01 февраля 2017, 23:21

Как в 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)
Answer 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

Answer 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, потом сравнивают длину на неравенство, и только потом - значение.

Answer 3

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

READ ALSO
Посоветуйте книгу либо ресурс [дубликат]

Посоветуйте книгу либо ресурс [дубликат]

На данный вопрос уже ответили:

477
Как в codeception последовательно сделать клик по нескольким элементам с зажатым 'shift'

Как в codeception последовательно сделать клик по нескольким элементам с зажатым 'shift'

Добрый деньтребуется выделить несколько блоков кликнув по каждому, но при этом должен быть зажат shift, т

481
Ajax laravel 5.3

Ajax laravel 5.3

ЗдравствуйтеСовсем недавно начал учить фреймворк laravel 5

732
Парсинг Вики-текста на PHP

Парсинг Вики-текста на PHP

Как можно пропарсить викитекст, используя API MediaWiki? Нашел вот эту статью : https://wwwmediawiki

506