С# в порядке со сравнением типов значений с нулевым
Я столкнулся с этим сегодня и понятия не имею, почему компилятор С# не выдает ошибку.
Int32 x = 1;
if (x == null)
{
Console.WriteLine("What the?");
}
Я смущен относительно того, как x может когда-либо быть нулевым. Тем более, что это назначение определенно вызывает ошибку компилятора:
Int32 x = null;
Возможно ли, что x может стать нулевым, не решила ли Microsoft не помещать эту проверку в компилятор, или это было пропущено полностью?
Обновление: после использования кода, чтобы написать эту статью, внезапно у компилятора появилось предупреждение о том, что выражение никогда не будет истинным. Теперь я действительно потерян. Я помещал объект в класс, и теперь предупреждение ушло, но осталось с вопросом, может ли тип значения быть нулевым.
public class Test
{
public DateTime ADate = DateTime.Now;
public Test ()
{
Test test = new Test();
if (test.ADate == null)
{
Console.WriteLine("What the?");
}
}
}
Ответы
Ответ 1
Это законно, потому что разрешение перегрузки оператора имеет уникальный лучший оператор для выбора. Существует оператор ==, который принимает два значения NULL. Внутренний объект int преобразуется в значение null. Нулевой литерал преобразуется в значение с нулевым значением int. Поэтому это законное использование оператора == и всегда приводит к ложному.
Аналогично, мы также разрешаем вам говорить "if (x == 12.6)", который также всегда будет ложным. Внутренний является конвертируемым в double, буквально конвертируется в double, и, очевидно, они никогда не будут равными.
Ответ 2
Это не ошибка, так как есть преобразование (int?
); он генерирует предупреждение в приведенном примере:
Результат выражения всегда "false", поскольку значение типа "int" никогда не равно "null" типа "int?"
Если вы проверите IL, вы увидите, что он полностью удаляет недостижимую ветку - он не существует в сборке выпуска.
Обратите внимание, что не создает это предупреждение для настраиваемых структур с операторами равенства. Он использовался в версии 2.0, но не в компиляторе 3.0. Код по-прежнему удаляется (поэтому он знает, что код недоступен), но не генерируется предупреждение:
using System;
struct MyValue
{
private readonly int value;
public MyValue(int value) { this.value = value; }
public static bool operator ==(MyValue x, MyValue y) {
return x.value == y.value;
}
public static bool operator !=(MyValue x, MyValue y) {
return x.value != y.value;
}
}
class Program
{
static void Main()
{
int i = 1;
MyValue v = new MyValue(1);
if (i == null) { Console.WriteLine("a"); } // warning
if (v == null) { Console.WriteLine("a"); } // no warning
}
}
С IL (для Main
) - отметить все, за исключением того, что MyValue(1)
(который может иметь побочные эффекты) был удален:
.method private hidebysig static void Main() cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] valuetype MyValue v)
L_0000: ldc.i4.1
L_0001: stloc.0
L_0002: ldloca.s v
L_0004: ldc.i4.1
L_0005: call instance void MyValue::.ctor(int32)
L_000a: ret
}
это в основном:
private static void Main()
{
MyValue v = new MyValue(1);
}
Ответ 3
Тот факт, что сравнение никогда не может быть правдой, не означает, что это незаконно. Тем не менее, нет, тип значения может быть null
.
Ответ 4
Нет, Int32 x
никогда не станет null
.
Если вы сравниваете int с null то оператор сравнения, который принимает два значения.
Почему сравнение типа значения с нулевым является предупреждением? статья поможет вам.
Ответ 5
Тип значения не может быть null
, хотя он может быть равен null
(рассмотрите Nullable<>
). В вашем случае переменная int
и null
неявно отображаются в Nullable<Int32>
и сравниваются.
Ответ 6
Я подозреваю, что ваш конкретный тест просто оптимизируется компилятором, когда он генерирует IL, поскольку тест никогда не будет ложным.
Боковое примечание: Можно использовать Int32 Intell с нулевым значением? x вместо.
Ответ 7
Я предполагаю, что это потому, что "==" представляет собой синтаксический сахар, который фактически представляет собой вызов метода System.Object.Equals
, который принимает параметр System.Object
. Null по спецификации ECMA является особым типом, который, конечно, получен из System.Object
.
Вот почему есть только предупреждение.
Ответ 8
[EDITED: внесены предупреждения в ошибки, а операторы явно указали на значение с нулевым значением, а не на строковый хак.]
В соответствии с разумным предложением @supercat в комментарии выше, следующие перегрузки операторов позволяют вам генерировать ошибку о сравнении вашего пользовательского типа значений с нулевым.
Внедряя операторы, которые сравниваются с нулевыми версиями вашего типа, использование значения null в сравнении соответствует нулевой версии оператора, что позволяет генерировать ошибку через атрибут Obsolete.
Пока Microsoft не вернет нам предупреждение о компиляторе, я пойду с этим решением, спасибо @supercat!
public struct Foo
{
private readonly int x;
public Foo(int x)
{
this.x = x;
}
public override string ToString()
{
return string.Format("Foo {{x={0}}}", x);
}
public override int GetHashCode()
{
return x.GetHashCode();
}
public override bool Equals(Object obj)
{
return x.Equals(obj);
}
public static bool operator ==(Foo a, Foo b)
{
return a.x == b.x;
}
public static bool operator !=(Foo a, Foo b)
{
return a.x != b.x;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo a, Foo? b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo a, Foo? b)
{
return true;
}
[Obsolete("The result of the expression is always 'false' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator ==(Foo? a, Foo b)
{
return false;
}
[Obsolete("The result of the expression is always 'true' since a value of type 'Foo' is never equal to 'null'", true)]
public static bool operator !=(Foo? a, Foo b)
{
return true;
}
}
Ответ 9
Я думаю, что лучший ответ на почему компилятор принимает это для общих классов. Рассмотрим следующий класс...
public class NullTester<T>
{
public bool IsNull(T value)
{
return (value == null);
}
}
Если компилятор не принимал сравнения с null
для типов значений, тогда он существенно побил бы этот класс, имея неявное ограничение, связанное с его параметром типа (то есть оно будет работать только с типами, не основанными на стоимости).