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

Почему методы TimeSpan и Guid Structs сравниваются с NULL?

Я заметил, что некоторые .NET-структуры можно сравнить с null. Например:

  TimeSpan y = new TimeSpan();
        if (y == null)
            return;

будет компилироваться просто отлично (то же самое с конструкцией Guid).
Теперь я знаю, что stucts являются типами значений и что код выше не должен компилироваться, если нет перегрузки оператора ==, который принимает объект. Но, насколько я могу судить, нет. Я просмотрел класс с помощью Reflector, а также в документах на MSDN.
Оба из них реализуют следующие интерфейсы:

IComparable, IComparable<T>, IEquatable<T>

но, пытаясь внедрить те же интерфейсы, похоже, не помогло:

struct XX : IComparable, IComparable<XX>, IEquatable<XX> {
    public int CompareTo(Object obj) {
        return 0;
    }
    public int CompareTo (XX other){
        return 0;
    }
    public bool Equals (XX other){
        return false;
    }
    public override bool Equals(object value){
        return false;
    }
    public static int Compare(XX t1, XX t2){
        return 0;
    }
}

Я использую:.NET 2.0 Visual Studio 2005.

Кто-нибудь знает, в чем причина? Я просто пытаюсь получить лучшее понимание. Это не проблема, поскольку я знаю, что я не должен сравнивать структуры с нулем в любом случае.

4b9b3361

Ответ 1

Это оператор ==.

Класс TimeSpan имеет перегрузку оператора равенства:

public static bool operator ==(DateTime d1, DateTime d2)
{
     return (t1._ticks == t2._ticks);
}

Это само по себе не позволяет сравнивать с null, , но...

С появлением типов с нулевым значением каждая структура неявно конвертируется в свой тип с нулевым значением, поэтому, когда вы видите что-то вроде

TimeSpan y = new TimeSpan();
if (y == null)
    return;

Вы не видите, что это происходит:

TimeSpan y = new TimeSpan();
if ((Nullable<TimeSpan>)y == (Nullable<TimeSpan>)null)
    return;

Null получает неявное преобразование (неявное присваивание?), но не все объекты System.Object:

TimeSpan y = new TimeSpan();
object o = null;
if (y == o) //compiler error
    return;

Хорошо, но оператор равенства не принимает аргументы с нулевым значением, не так ли?

Ну, msdn поможет здесь:

Предопределенные унарные и двоичные операторы и любые пользовательские операторы, которые существуют для типов значений также могут использоваться типами с нулевым значением. Эти операторы производят нулевое значение если [любой из] операндов нулевых; в противном случае, оператор использует содержащееся значение для вычисления результата.

Итак, вы эффективно получаете версию с нулевым значением для каждого оператора бесплатно с фиксированным определенным поведением. Указанная выше "содержащаяся ценность" представляет собой фактическое значение, возвращаемое оператором, не подлежащим обнулению.

Ответ 2

Этот случай рассматривается для дженериков в разделе 7.9.6 спецификации языка С#.

Конструкция x == null допускается, даже если T может представлять тип значения, и результат просто определяется как false, когда T является типом значения.

Я немного выкопал спецификацию и не смог найти более общее правило. Jon указывает на то, что это проблема с нулевым продвижением.

Это правило (или аналогичное изменение), похоже, применяется здесь. Если вы внимательно посмотрите на отраженный результат, вы заметите, что сравнения там нет. Компилятор С#, по-видимому, оптимизирует это сравнение и заменяет его ложным.

Например, если вы наберете следующий

var x = new TimeSpan();
var y = x == null;
Console.WriteLine(x);

Затем декомпилируйте его, вы увидите следующее

var x = new TimeSpan();
var y = false;
Console.WriteLine(x);

Ответ 3

Эта проблема была эффективно введена при включении типов с нулевым значением. Там неявное преобразование из TimeSpan в TimeSpan?, и есть сравнение между TimeSpan? и нулевым значением этого типа.

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

int x = 10;
if (x == null)
{
    Console.WriteLine();
}

Предоставляет это предупреждение:

Test.cs(9,13): warning CS0472: The result of the expression is always 'false'
       since a value of type 'int' is never equal to 'null' of type 'int?'

Я считаю, что Марк Гравелл и я разработали обстоятельства, при которых предупреждение дается один раз... стыдно, что это непротиворечиво.

Ответ 4

См. также: С# 3 (.NET 3.5) версия csc не сообщает CS0162 для неуправляемого кода (struct/null)

Начиная с компилятора С# 3, который означает, что он иногда даже не предупреждает вас об этом; -p

Поскольку Guid/TimeSpan и т.д. предоставляют ==, они попадают в эту ловушку, где она не предупреждает вас.

Ответ 5

Я нашел IT:)

Ниже приводится предупреждение:

int i = 0;
if (i == null)
// ^^ Warning: The result of the expression is always 'false' since a value of
//             type 'int' is never equal to 'null' of type 'int?'

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

Изменить: связанный раздел в спецификации - это §13.7.1, в котором указано, что null может быть неявно преобразован в любой тип с нулевым типом и (очень трудный для чтения) раздел § 13.7.2 с указанием типа значения T может быть неявно преобразован в T?.

Что я изначально писал:

Что бы ни случилось в С#, потому что, как JaredPar говорит, что он компилируется просто false.

Обратите внимание, что это не скомпилируется:

TimeSpan ts = new TimeSpan();
object o = null;
if (ts == o) // error, Operator '==' cannot be applied to operands of type 'System.TimeSpan' and 'object'
    ...

Ответ 6

TimeSpan span = TimeSpan.Zero;