C# хорошо при сравнении типов значений с null
я столкнулся с этим сегодня и понятия не имею, почему компилятор C# не выдает ошибку.
Int32 x = 1;
if (x == null)
{
Console.WriteLine("What the?");
}
Я в замешательстве относительно того, как x может когда-либо быть нулевым. Тем более, что это назначение определенно вызывает ошибку компилятора:
Int32 x = null;
возможно ли, что x может стать null, Microsoft просто решила не помещать эту проверку в компилятор или она была полностью пропущена?
обновление: после возиться с кодом, чтобы написать эту статью, вдруг компилятор пришел с предупреждением, что выражение никогда не будет истинным. Теперь я действительно потерялся. Я поместил объект в класс, и теперь предупреждение ушло, но осталось с вопросом, Может ли тип значения оказаться нулевым.
public class Test
{
public DateTime ADate = DateTime.Now;
public Test ()
{
Test test = new Test();
if (test.ADate == null)
{
Console.WriteLine("What the?");
}
}
}
10 ответов:
Это законно, потому что разрешение перегрузки оператора имеет уникальный Лучший оператор на выбор. Существует оператор==, который принимает два nullable ints. Int local преобразуется в nullable int. Литерал null преобразуется в nullable int. Поэтому это законное использование оператора==, и всегда будут ложными.
аналогично, мы также позволяем вам сказать " если (x == 12.6)", что также всегда будет ложным. Int local преобразуется в double, литерал-это конвертируемые в двойные, и очевидно, что они никогда не будут равны.
это не ошибка, так как есть (
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); }
тот факт, что сравнение никогда не может быть истинным, не означает, что это незаконно. Тем не менее, нет, тип значения никогда не может быть
null.
нет,
Int32 xникогда не станетnull.Если вы сравниваете int с null тогда оператор сравнения, что принимает два int?s применимо.
" почему сравнение типа значения с null является предупреждением?" статья поможет вам.
тип значения не может быть
null, хотя это может быть равноnull(считатьNullable<>). В вашем случаеintпеременной иnullнеявно приводятся кNullable<Int32>и по сравнению.
Я подозреваю, что ваш конкретный тест просто оптимизируется компилятором, когда он генерирует IL, так как тест никогда не будет ложным.
боковое Примечание:Можно ли использовать Int32 с нулевым значением Int32? x вместо этого.
Я думаю, это потому, что "= = " - это синтаксический сахар, который на самом деле представляет собой вызов
System.Object.Equalsметод, который принимает
[отредактировано: сделаны предупреждения на ошибки, и делали операторы четко, допускающий значение null вместо строки взломать.]
согласно умному предложению @supercat в комментарии выше, следующие перегрузки оператора позволяют вам генерировать ошибку о сравнении вашего пользовательского типа значения с null.
реализуя операторы, которые сравниваются с nullable версиями вашего типа, использование null в сравнении соответствует nullable версии оператора , что позволяет генерировать ошибку с помощью устаревшего атрибута.
пока 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; } }
Я думаю, что лучший ответ почему компилятор принимает это для универсальных классов. Рассмотрим следующий класс...
public class NullTester<T> { public bool IsNull(T value) { return (value == null); } }если компилятор не принимает сравнения с
nullдля типов значений, то он по существу сломает этот класс, имея неявное ограничение, привязанное к его параметру типа (т. е. он будет работать только с типами, не основанными на значениях).
компилятор позволит вам сравнить любую структуру, реализующую
==к нулю. Это даже позволяет сравнить int с null (вы получите предупреждение, хотя).но если вы разберете код, вы увидите, что сравнение решается при компиляции кода. Так, например, этот код (где
Foo- это структура реализации==):public static void Main() { Console.WriteLine(new Foo() == new Foo()); Console.WriteLine(new Foo() == null); Console.WriteLine(5 == null); Console.WriteLine(new Foo() != null); }генерирует этот IL:
.method public hidebysig static void Main() cil managed { .entrypoint // Code size 45 (0x2d) .maxstack 2 .locals init ([0] valuetype test3.Program/Foo V_0) IL_0000: nop IL_0001: ldloca.s V_0 IL_0003: initobj test3.Program/Foo IL_0009: ldloc.0 IL_000a: ldloca.s V_0 IL_000c: initobj test3.Program/Foo IL_0012: ldloc.0 IL_0013: call bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo, valuetype test3.Program/Foo) IL_0018: call void [mscorlib]System.Console::WriteLine(bool) IL_001d: nop IL_001e: ldc.i4.0 IL_001f: call void [mscorlib]System.Console::WriteLine(bool) IL_0024: nop IL_0025: ldc.i4.1 IL_0026: call void [mscorlib]System.Console::WriteLine(bool) IL_002b: nop IL_002c: ret } // end of method Program::Mainкак вы можете смотрите:
Console.WriteLine(new Foo() == new Foo());переведено на:
IL_0013: call bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo, valuetype test3.Program/Foo)при этом:
Console.WriteLine(new Foo() == null);переводится как false:
IL_001e: ldc.i4.0