Подтвердить что ты не робот

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

Вот небольшой фрагмент кода:

String a = "abc";

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

Почему?

4b9b3361

Ответ 1

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

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. Для ссылочных типов, отличных от строки, == возвращает true, если два операнда относятся к одному и тому же объекту. Для типа string == сравнивает значения строк.

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

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

где

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

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

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

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

ceq

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

Хольгер спросил, почему компилятор делает только добавление между двумя строковыми литералами... Теперь, строго соблюдая спецификации С# 5.0, и учитывая, что спецификации С# 5.0 должны быть "отделены" от спецификаций .NET(за исключением предпосылок, которые С# 5.0 имеет для некоторых классов /structs/methods/properties/...), мы имеем:

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

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

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

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

Ответ 2

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