Почему методы 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.
Кто-нибудь знает, в чем причина?
Я просто пытаюсь получить лучшее понимание. Это не проблема, поскольку я знаю, что я не должен сравнивать структуры с нулем в любом случае.
Ответы
Ответ 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;