Сравнение структур к пустому указателю [дубликат]

37
задан Community 23 May 2017 в 11:54
поделиться

6 ответов

Похоже, что проблема в том, что когда MS ввел нулевые типы, они сделали так, что каждая структура неявно преобразуется в свой нулевой тип (foo? ), так что код

if( f == null)

эквивалентен

if ( (Nullable<foo>)f == (Nullable<foo>)null) 

Поскольку в MSDN сказано, что "любые пользовательские операторы, существующие для типов значений, могут также использоваться нулевыми типами", когда вы переопределяете оператор ==, вы позволяете неявное приведение компилировать, так как теперь у вас есть пользовательский == -- дающий вам нулевую перегрузку бесплатно.

В стороне:

Похоже, что в вашем примере есть некоторая оптимизация компилятора. Единственное, что выдает компилятор, который даже намекает, что был тест, это IL:

ldc.i4.0
ldc.i4.0
ceq
stloc.1   //where there is an unused boolean local

Обратите внимание, что если поменять main на

Foo f = new Foo();
object b = null;
if (f == b) { Console.WriteLine("?"); }

, то он больше не будет компилироваться. Но если вы установите флажок struct:

Foo f = new Foo();
object b = null;
if ((object)f == b) { Console.WriteLine("?"); }

если компилирует, то испускает IL, и запускается, как и ожидалось (структура никогда не бывает нулевой);

.
18
ответ дан 27 November 2019 в 05:02
поделиться

struct не определяет перегрузки "==" или "!=", поэтому вы получили оригинальную ошибку. После добавления перегрузок в Вашу структуру сравнение было легальным (с точки зрения компилятора). Как создатель перегрузки оператора, Вы отвечаете за обработку этой логики (очевидно, Microsoft пропустила это в данном случае).

В зависимости от реализации Вашей структуры (и того, что она собой представляет) сравнение с нулем может быть вполне корректным, вот почему это возможно.

.
1
ответ дан 27 November 2019 в 05:02
поделиться
-1
ответ дан 27 November 2019 в 05:02
поделиться

Все, что я могу подумать, это то, что ваша перегрузка оператора == дает компилятору выбор между:

public static bool operator ==(object o1, object o2)

и

public static bool operator ==(Foo f1, Foo f2)

и что с помощью обоих вариантов он может привести левую к объекту и использовать первую. Конечно, если вы попытаетесь запустить что-то, основанное на вашем коде, это не приведет к перегрузке оператора. Не имея выбора между операторами, компилятор явно проводит некоторую дополнительную проверку.

4
ответ дан 27 November 2019 в 05:02
поделиться

Я полагаю, что когда вы перегружаете оператора, вы явно подписываетесь на то, что будете обрабатывать всю необходимую логику с конкретным оператором. Следовательно, Вы отвечаете за обработку нуля в методе перегрузки оператора, если он когда-нибудь получит удар. В данном случае, как я уверен, Вы наверняка заметили, перегруженные методы никогда не получат удара, если сравнивать с нулем.

Что действительно интересно, так это то, что после ответа Хенкс здесь , я проверил в рефлекторе следующий код.

Foo f1 = new Foo();
if(f1 == null)
{
  Console.WriteLine("impossible");
}

Console.ReadKey();

Это то, что показал рефлектор.

Foo f1 = new Foo();
Console.ReadKey();

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

1
ответ дан 27 November 2019 в 05:02
поделиться

Это не имеет ничего общего с сериализацией или COM - так что стоит убрать это из уравнения. Например, вот короткая, но полная программа, которая демонстрирует проблему:

using System;

public struct Foo
{
    // These change the calling code's correctness
    public static bool operator ==(Foo f1, Foo f2) { return false; }
    public static bool operator !=(Foo f1, Foo f2) { return false; }

    // These aren't relevant, but the compiler will issue an
    // unrelated warning if they're missing
    public override bool Equals(object x) { return false; }
    public override int GetHashCode() { return 0; }
}

public class Test
{
    static void Main()
    {
        Foo f = new Foo();
        Console.WriteLine(f == null);
    }
}

Я считаю, что это компилируется, потому что существует неявное преобразование из нулевого литерала в Nullable , и вы можете сделайте это законно:

Foo f = new Foo();
Foo? g = null;
Console.WriteLine(f == g);

Интересно, что это происходит только тогда, когда == перегружен - Марк Гравелл заметил это раньше. Я не знаю, действительно ли это ошибка компилятора или что-то очень тонкое в способе разрешения преобразований, перегрузок и т. Д.

В некоторых случаях (например, int , decimal ) компилятор предупредит вас о неявном преобразовании, но в других случаях (например, Guid ) нет.

6
ответ дан 27 November 2019 в 05:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: