2012-02-08 3 views
4

Если я перегрузку operator == для класса, я должен выполнить некоторые проверки, прежде чем сравнивать поля:оператор равенства перегрузки в структура и классы

  • если оба аргумента равны нулю, или оба аргумента являются одинаковыми экземпляр, затем возвращает истинное

    Пример: if (System.Object.ReferenceEquals(arg1, arg2)) return true;

  • если один пустой, но не оба, а затем возвращает ложь

    Пример: if (((object)arg1 == null) || ((object)arg2 == null)) return false;

В самом деле, если у меня есть структура и я хочу сделать перегрузку operator ==, эти проверки не нужны, а они бесполезны, по следующим причинам: а структура является значение типа, поэтому оно не может быть нулевым, например DateTime date = null; недействительно, потому что DateTime (это struct) не является ссылочным типом, поэтому вы не можете сравнить два DateTime, один из которых установлен в null.

Я создал простой-структуру Point2D с operator ==, то я сравниваю экземпляр Point2D с null:

Point2D point = new Point2D(0,0); 
Console.WriteLine((point == null)); 
  1. Очевидно operator == это не называется, но сравнение возвращает False. Какой метод называется?

  2. В заявке documentation указано, что перегрузка этого оператора в не неизменяемых типах не рекомендуется. Зачем?

+1

Вы должны попытаться задать только один вопрос одновременно. Если у вас есть два вопроса, спросите их отдельно. – svick

+0

@svick: Извините. Для следующих вопросов я избегу этого. – enzom83

ответ

5

Потому что кажется, что компилятор оптимизирует это. Я попробовал этот код:

System.Drawing.Point point = new System.Drawing.Point(0,0); 
Console.WriteLine((point == null)); 

И генерируется следующий IL:

IL_0000: ldloca.s 00 
IL_0002: ldc.i4.0  
IL_0003: ldc.i4.0  
IL_0004: call  System.Drawing.Point..ctor 
IL_0009: ldc.i4.0  
IL_000A: call  System.Console.WriteLine 

Это в конечном счете сводится к «Создать точку, а затем записать ложные в командной строке»

Этот также объясняет, почему он не вызывает вашего оператора. Структура никогда не может быть нулевой, и в тех случаях, когда компилятор может гарантировать, что вы всегда будете получать false в результате, он не беспокоит выдачу кода для вызова оператора вообще.

То же самое происходит с этим кодом, даже если строка является класс и перегрузить оператор ==:

System.Drawing.Point point = new System.Drawing.Point(0,0); 
Console.WriteLine("foo" == null); 

Что касается неизменности ... Оператор == в C#, как правило, интерпретируется означает " ссылочное равенство ", например эти две переменные указывают на один и тот же экземпляр класса. Если вы перегружаете его, то обычно вы говорите, что два экземпляра класса, а не тот же экземпляр, должны вести себя так, как если бы они были тем же самым экземпляром, когда их данные одинаковы. Классическим примером является Strings."A" == GiveMeAnA(), хотя фактическая ссылка на строку, возвращаемая GiveMeAnA, может быть не такой, как та, которая представлена ​​буквами "A".

Если вы перегрузили оператора == по классам, которые не были неизменными, то была выполнена оценка мутации класса после ==, что может привести к многочисленным тонким ошибкам.

+0

Как я могу увидеть ИЛ? – enzom83

+3

Я использую LINQPad (http://linqpad.net) или Reflector (http://reflector.net). Первый показывает вам IL произвольных фрагментов кода, последний декомпилирует сборки в IL и может повторно генерировать эквивалентный C# из этого IL. Существует также встроенный инструмент под названием ILDASM (http://msdn.microsoft.com/en-us/library/f7dy01k1(v=vs.80).aspx). Понимание ИЛ - сложная вещь - см. Http: // codebetter.com/raymondlewallen/2005/02/07/getting-started-understanding-msil-assembly-language/ –

12

Ответ Криса Шаина правильный, но не объясняет , почему это законно.

Когда переопределить оператор равенства, и оба операнд Ненулевых типов значений и тип возвращения BOOL, то бесплатно мы даем вам подняло оператора. То есть, если у вас есть

public static bool operator ==(S s1, S s2) { ... } 

затем для каких-либо дополнительных затрат вы получите

public static bool operator ==(S? s1, S? s2) { ... } 

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

Раньше было предупреждение о том, что ваш код всегда возвращает false, но мы случайно отключили его пару версий и никогда не возвращали его обратно. Завтра я работаю над этим кодом в компиляторе Roslyn, поэтому я посмотрю, что я могу сделать, чтобы вернуть его в форму.

+3

Чтение ваших ответов всегда образовательное, даже после написания кода .NET в течение почти десятилетия. Спасибо :-) –

+0

Эрик, вы говорите, что здесь у нас есть два операнда типа 'Point' и' null'; между ними нет неявного преобразования, но компилятор вызывает оператор для другого типа, к которому оба операнда могут быть неявно преобразованы. Это кажется несовместимым с обработкой условного оператора, где вы не можете сказать 'int? x = someBool? 7: null; 'Я начал с версии 2 из C#; было ли выражение, подобное «1 == null», не удалось скомпилировать в более ранних версиях? – phoog

+0

@phoog: Это не совсем правильная аналогия. Правильная аналогия, обеспечивающая * перегрузку оператором *, - * разрешение перегрузки * метода *. Если у вас есть статический метод 'int? Operators.Add (int ?, int?) ', И вы вызываете' Operator.Add (1, null) ', тогда вы ожидаете, что этот метод будет выбран, хотя int не конвертируется в null и null не преобразуется в int , правильно? Подумайте о том, что все встроенные и снятые операторы являются статическими методами для операторов класса; разрешение перегрузки оператора просто делает перегрузочное разрешение для этих методов. –

Смежные вопросы