C#好的比较值types为null
我今天遇到了这个,不知道为什么C#编译器不会抛出一个错误。
Int32 x = 1; if (x == null) { Console.WriteLine("What the?"); }
我很困惑x怎么可能是空的。 尤其是因为这个赋值绝对会引发一个编译错误:
Int32 x = null;
x是否可能变为null,Microsoft是否决定不把这个检查放入编译器,还是完全错过?
更新:在编写这篇文章的代码搞乱之后,编译器突然想出了一个警告,expression式永远不会是真的。 现在我真的迷失了。 我把对象放到一个类中,现在警告已经消失了,但是留下了一个问题,值types最终可能为null。
public class Test { public DateTime ADate = DateTime.Now; public Test () { Test test = new Test(); if (test.ADate == null) { Console.WriteLine("What the?"); } } }
这是合法的,因为操作员重载分辨率具有唯一的最佳操作员select。 有一个==运算符,需要两个可为空的整数。 int本地可转换为可空int。 null文字可转换为可空int。 因此,这是==运算符的合法用法,并且总是会导致错误。
同样,我们也允许你说“if(x == 12.6)”,这也总是假的。 int本地可以转换为double,文字可以转换为double,显然它们永远不会相等。
这不是一个错误,因为有一个( int?
)转换; 它会在给出的例子中产生一个警告:
由于types'int'的值永远不会等于'int'types的'null',所以expression式的结果总是'false'。
如果你检查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.sv 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); }
事实上,比较永远不会是真的并不意味着这是非法的。 尽pipe如此,不,值types可以为null
。
不, Int32 x
不会变为null
。
如果您将int与null进行比较,则接受两个int的比较运算符是适用的。
“为什么将一个值types与null比较是一个警告? 文章会帮助你。
值types不能为null
,尽pipe它可能等于null
(考虑Nullable<>
)。 在你的情况下, int
variables和null
被隐式转换为Nullable<Int32>
并进行比较。
我怀疑你的特定testing正在被编译器在生成IL时进行优化,因为testing永远不会是错误的。
附注:Int32可以有一个可以为空的Int32吗? x代替。
我想这是因为“==”是一个语法糖,它实际上代表了调用System.Object.Equals
方法来接受System.Object
参数。 ECMA规范中的NULL是一个特殊的types,当然是从System.Object
派生的。
这就是为什么只有一个警告。
[编辑:警告错误,并使操作员明确可空,而不是string黑客]。
根据上面的注释中的@ supercat聪明的build议,以下运算符重载允许您生成有关您的自定义值types比较为null的错误。
通过实现与您的types的可为空的版本进行比较的运算符,在比较中使用null与运算符的可为空版本相匹配,从而可以通过Obsolete属性生成错误。
直到微软给我们回来我们的编译器警告我要与这个解决方法,谢谢@超级猫!
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 ax == bx; } public static bool operator !=(Foo a, Foo b) { return ax != bx; } [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; } }
我想为什么编译器接受这个最好的答案是generics类。 考虑以下课程…
public class NullTester<T> { public bool IsNull(T value) { return (value == null); } }
如果编译器不接受与值types的null
比较,那么它本质上会破坏这个类,并且有一个隐含的约束附加到它的types参数(也就是说它只能用于非基于值的types)。