C#. Непоследовательное сравнение

от автора


От переводчика:
Это вольный перевод блогозаписи Эрика Липперта (Eric Lippert), в прошлом одного из разработчиков языка C#. Запись оформлена в виде «вопрос-ответ», я пропущу вопрос и перейду к ответу, вы можете ознакомиться с вопросом в оригинале, но там ничего особо интересного.

Но, для начала, я попрошу взглянуть на следующий код, и без гугла и компилирования, попробовать выяснить что произойдет в 9 случаях сравнения и сравнить с ответами (для опроса):

int myInt = 1; short myShort = 1; object objInt1 = myInt; object objInt2 = myInt; object objShort = myShort; Console.WriteLine(myInt == myShort);          // scenario 1 Console.WriteLine(myShort == myInt);          // scenario 2 Console.WriteLine(myInt.Equals(myShort));     // scenario 3 Console.WriteLine(myShort.Equals(myInt));     // scenario 4 Console.WriteLine(objInt1 == objInt1);        // scenario 5 Console.WriteLine(objInt1 == objShort);       // scenario 6 Console.WriteLine(objInt1 == objInt2);        // scenario 7 Console.WriteLine(Equals(objInt1, objInt2));  // scenario 8 Console.WriteLine(Equals(objInt1, objShort)); // scenario 9 



Язык C# был спроектирован так, чтобы работать так, как этого ожидает разработчик: то есть, язык где очевидные техники и правильные техники одно и тоже. И по большей части это так. К сожалению, сравнение это одна из частей языка, в которой есть ловушки.

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

Ответы

int myInt = 1; short myShort = 1; object objInt1 = myInt; object objInt2 = myInt; object objShort = myShort; Console.WriteLine(myInt == myShort);          // scenario 1 true Console.WriteLine(myShort == myInt);          // scenario 2 true Console.WriteLine(myInt.Equals(myShort));     // scenario 3 true Console.WriteLine(myShort.Equals(myInt));     // scenario 4 false! Console.WriteLine(objInt1 == objInt1);        // scenario 5 true Console.WriteLine(objInt1 == objShort);       // scenario 6 false!! Console.WriteLine(objInt1 == objInt2);        // scenario 7 false!!! Console.WriteLine(Equals(objInt1, objInt2));  // scenario 8 true Console.WriteLine(Equals(objInt1, objShort)); // scenario 9 false!?! 

Что за черт? Разберем все по-порядку.

В первом и втором случае, мы должны вначале определить что значит оператор ==. В C# существует более десятка встроенных операторов == для сравнения различных типов.

object == object string == string int == int uint == uint long == long ulong == ulong ... 

Так как не существует операторов int == short или short == int, должен быть выбран самый подходящий оператор. В нашем случае это оператор int == int. Таким образом, short конвертируется в int и затем две переменные сравниваются по значению. Следовательно, они равны.

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

Equals(object, object) // статический метод унаследованный от object Equals(object)         // виртуальный метод унаследованный от object Equals(int)            // реализация метода интерфейса IEquatable<int>.Equals(int) 

Первый нам не подходит потому что у нас недостаточно аргументов для его вызова. Из двух других методов, больше подходит метод который принимает int как параметр, всегда лучше сконвертировать аргумент типа short в int, чем в object. Следовательно, будет вызван Equals(int), который сравнивает две переменные типа int используя сравнение по значению, таким образом это выражение истинно.

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

Equals(object, object) // статический метод унаследованный от object Equals(object)         // виртуальный метод унаследованный от object Equals(short)          // реализация метода интерфейса IEquatable<short>.Equals(short) 

Первый и третий методы нам не подходят, потому что для первого у нас слишком мало аргументов, а третий метод не будет выбран потому что нет неявного приведения типа int к short. Остается метод short.Equals(object), реализация которого равна следующему коду:

bool Equals(object z) {   return z is short && (short)z == this; } 

То есть, чтобы этот метод вернул true, упакованный элемент должен являться типом short, и после распаковки он должен равняться экземпляру который вызвал Equals. Но, так как, передаваемый аргумент является типом int, метод вернет false.

В пятом, шестом и седьмом, будет выбрана форма сравнения object == object, что эквивалентно вызову метода Object.ReferenceEquals. Очевидно, что две ссылки равны в пятом случае и неравны в шестом и седьмом. Значения которые содержатся в переменных типа object неважны, потому что сравнение по значению не используется совсем, сравниваются только ссылки.

В восьмом и девятом случае, будет использован статический метод Object.Equals, который реализован следующим образом:

public static bool Equals(object x, object y) {     if (ReferenceEquals(x, y)) return true;     if (ReferenceEquals(x, null)) return false;     if (ReferenceEquals(y, null)) return false;     return x.Equals(y); } 

В восьмом случае, мы имеем две ссылки которые не равны и не равны null, поэтому, будет вызван int.Equals(object), который как вы можете предположить смотря на код метода short.Equals(object), реализован следующим образом:

bool Equals(object z) {   return z is int && (int)z == this; } 

Так как аргумент является упакованной переменной типа int, будет произведено сравнение по значению и метод вернет true. В девятом случае упакованная переменная имеет тип short, следовательно проверка типа (z is int) завершится неудачей, и метод вернет false.

Итог:
Я показал девять различных способов сравнения двух переменных, несмотря на то, что во всех случаях, сравниваемые переменные равны единице, только в половине случаев сравнение возвращает true. Если вы думаете, что это сумасшествие и все запутанно, вы правы! Сравнение в C# очень коварно.

Сколько результатов сравнений для вас оказались неожиданными?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/209412/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *