Почему эти два сравнения строк дают разные результаты?

Вот небольшой кусочек кода:

String a = "abc";

Console.WriteLine(((object)a) == ("ab" + "c")); // true 
Console.WriteLine(((object)a) == ("ab" + 'c')); // false 

Зачем?

2 ответа

Решение

Поскольку == делает эталонное сравнение. С помощью компилятора C# все "равные" строки, известные во время компиляции, "группируются" вместе, так что

string a = "abc";
string b = "abc";

будет указывать на ту же строку "abc". Таким образом, они будут равны.

Сейчас, ("ab" + "c") упрощается во время компиляции "abc", в то время как "ab" + 'c' не является и, следовательно, не является ссылочно равным (операция конкатенации выполняется во время выполнения).

Смотрите декомпилированный код здесь

Я добавлю, что Try Roslyn делает неправильную декомпиляцию:-) И даже IlSpy:-(

Это декомпилируется в:

string expr_05 = "abc"
Console.WriteLine(expr_05 == "abc");
Console.WriteLine(expr_05 == "ab" + 'c');

Так что сравнение строк. Но по крайней мере тот факт, что некоторые строки вычисляются во время компиляции, хорошо виден.

Почему ваш код выполняет сравнение ссылок? Потому что вы кастуете одного из двух участников objectи operator== в.NET нет virtual, поэтому он должен быть разрешен во время компиляции с информацией, имеющейся у компилятора, а затем... из == оператора

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

Для компилятора первый операнд == оператор не string (потому что ты его наложил), поэтому он не падает в string сравнение.

Интересный факт: на уровне CIL (язык ассемблера.NET) используется код операции ceq, это делает сравнение значений для примитивных типов значений и сравнение ссылок для ссылочных типов (поэтому, в конце концов, оно всегда выполняет побитовое сравнение, с некоторыми исключениями для типов с плавающей точкой с NaN). Он не использует "специальные" operator== методы. Это можно увидеть в этом примере

где

Console.WriteLine(a == ("ab" + 'c')); // True 

решается во время компиляции в вызове

call bool [mscorlib]System.String::op_Equality(string, string)

в то время как другой == просто

ceq

Это объясняет, почему декомпилятор Roslyn работает "плохо" (как IlSpy:-(, см. Отчет об ошибке)... Он видит код операции ceq и не проверяет, есть ли приведение, необходимое для восстановления правильного сравнения.

Хольгер спросил, почему компилятором выполняется только сложение между двумя строковыми литералами... Теперь, очень строго читая спецификации C# 5.0 и считая, что спецификации C# 5.0 "отделены" от спецификаций.NET (с Исключения из предварительных условий, которые C# 5.0 имеет для некоторых классов / Struct / Methods / Properties /...), мы имеем:

Конкатенация строк:

string operator +(string x, string y);
string operator +(string x, object y);
string operator +(object x, string y);

Эти перегрузки бинарного оператора + выполняют конкатенацию строк. Если операнд конкатенации строк равен нулю, подставляется пустая строка. В противном случае любой нестроковый аргумент преобразуется в его строковое представление, вызывая виртуальный метод ToString, унаследованный от объекта типа. Если ToString возвращает ноль, подставляется пустая строка.

Итак, дело string + string, string + null, null + string все они точно описаны, и их результат может быть "вычислен" с использованием только правил спецификаций C#. Для любого другого типа virtual ToString метод должен быть вызван. Результат virtual ToString Метод не определен ни для какого типа в спецификациях C#, поэтому, если компилятор "предполагал" свой результат, он сделал бы неправильную "вещь". Например, версия.NET, которая имела System.Boolean.ToString() что вернулся Yes/No вместо True/False все еще будет в порядке для спецификаций C#.

Адрес не тот же. если вы хотите сравнить строковый символ, предложите использовать равно.

Другие вопросы по тегам