Почему эти два сравнения строк дают разные результаты?
Вот небольшой кусочек кода:
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#.
Адрес не тот же. если вы хотите сравнить строковый символ, предложите использовать равно.