Поведение String.Equals и ==

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

Мне нужен вывод поведения String.Equals и ==.

bool result = false;

    object obj = "String"; 
    string str2 = "String";
    string str3 = typeof(string).Name;
    string str4 = "String";
    object obj2 = str3; 

    // obj, str2, str4 references are same.
    // obj is object type and others are string type

    // Comparision between object obj and string str2 -- Com 1
    result = String.Equals(obj, str2);// true
    result = String.ReferenceEquals(obj, str2); // true
    result = (obj == str2);// true

    // Comparision between object obj and string str3 -- Com 2
    result = String.Equals(obj, str3);// true
    result = String.ReferenceEquals(obj, str3); // false
    result = (obj == str3);// false

    // Comparision between object obj and string str4 -- Com 3
    result = String.Equals(obj, str4);// true
    result = String.ReferenceEquals(obj, str4); // true
    result = (obj == str4);// true

    // Comparision between string str2 and string str3 -- Com 4
    result = String.Equals(str2, str3);// true
    result = String.ReferenceEquals(str2, str3); // false
    result = (str2 == str3);// true

    // Comparision between string str2 and string str4 -- Com 5
    result = String.Equals(str2, str4);// true
    result = String.ReferenceEquals(str2, str4); // true
    result = (str2 == str4);// true

    // Comparision between string str3 and string str4 -- Com 6
    result = String.Equals(str3, str4);// true
    result = String.ReferenceEquals(str3, str4); // false
    result = (str3 == str4);// true

    // Comparision between object obj and object obj2 -- Com 7
    result = String.Equals(obj, obj2);// true
    result = String.ReferenceEquals(obj, obj2); // false
    result = (obj == obj2);// false

Я смотрю также на часы

obj     "String" {1#}   object {string}
str2    "String" {1#}   string
str3    "String" {6#}   string
str4    "String" {1#}   string
obj2    "String" {6#}   object {string}

и прочитайте статьи здесь и здесь

Почему Com1, Com2, Com3, Com4, Com5 и Com6 имеют разные типы поведения?

Ответ 1

Оператор == имеет различное поведение для strings и всех других типов ссылок. Если оба операнда == равны string, то используется сравнение String.Equals. В противном случае == эквивалентно Object.ReferenceEquals

Из С# == Документация оператора:

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

Другим эффектом, который вы видите, является string intering. В вашем коде есть только одна ссылка на литеральное значение "String". Вы код эквивалентен: obj = str2 = str4 = "String". obj, str2 и str4 - все ссылки на одну и ту же базовую строку. Поскольку str3 генерируется во время выполнения, она устанавливается в другую строку, которая имеет такое же значение "String".

Подводя итог:

  • com1: result = (obj == str2);//true
    • сравнивает object и string, поэтому выполняет проверку равенства ссылок
    • obj и str2 указывают на одну и ту же ссылку, поэтому результат верен.
  • result = (obj == str3);//false
    • сравнивает object и string, поэтому выполняет проверку равенства ссылок
    • obj и str3 указывают на разные ссылки, поэтому результат ложный
  • result = (obj == str4);//true
    • сравнивает object и string, поэтому выполняет проверку равенства ссылок
    • obj и str4 указывают на одну и ту же ссылку, поэтому результат верен.
  • result = (str2 == str3);//true
    • сравнивает string и string, поэтому выполняет проверку значения строки
    • str2 и str3 являются "String", поэтому результат верен.
  • result = (str2 == str4);//true
    • сравнивает string и string, поэтому выполняет проверку значения строки
    • str2 и str4 являются "String", поэтому результат верен.
  • result = (str3 == str4);//true
    • сравнивает string и string, поэтому выполняет проверку значения строки
    • str3 и str4 являются "String", поэтому результат верен.
  • result = (obj == obj2);//false - сравнивает object и object, поэтому выполняет контрольную проверку равенства - obj и obj2 указывают на разные ссылки, поэтому результат ложный

Помните, что тип сравнения, который выполняет ==, выбирается во время компиляции, поэтому он основан на статическом типе операндов. Компилятор не знает, что obj и obj2 всегда будут указывать на строки.

Ответ 2

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

Метод Equals на String сравнивает значение строки с другим объектом (который должен быть String).

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

Наконец, оператор равенства проверяет равенство между значениями String s. В случае

typeof(string).Name == (object)"String"

результат возвратит false, так как левый операнд на самом деле является константой, встроенной в определение типа строки, и не использует одну и ту же ссылку на строковый литерал, и поскольку правый операнд помещен в бокс, сравниваются их ссылки.