我就遇到了这个今天不知道为什么C#编译器不会抛出一个错误。
Int32 x = 1;
if (x == null)
{
Console.WriteLine("What the?");
}
我很困惑,如何X可能永远不可能为空。 特别是因为这种分配绝对抛出一个编译器错误:
Int32 x = null;
有没有可能是X可能成为空,没有微软刚刚决定不把这个检查到编译器中,或者被它完全错过了什么?
更新:与代码搞乱写这篇文章后,突然编译想出了表达永远不会是真实的警告。 现在我真的失去了。 我把对象变成一个类,现在报警已经消失,但留下的问题,可以在值类型最终被空。
public class Test
{
public DateTime ADate = DateTime.Now;
public Test ()
{
Test test = new Test();
if (test.ADate == null)
{
Console.WriteLine("What the?");
}
}
}
Answer 1:
这是合法的,因为运算符重载决策必须选择一个唯一的最好的运营商。 有一个==操作符带有两个可空整数。 的int地方可以转换为可空int类型。 null文本可以转换为可空int类型。 因此,这是==操作符的合法使用,而且将永远导致错误。
同样,我们也允许你说:“如果(X == 12.6)”,这也将永远是假的。 的int地方可以转换为双,文字可以转换为双,显然他们将永远是平等的。
Answer 2:
这不是错误,因为有一个( int?
转换; 它产生于给出的例子警告:
表达的结果始终是因为类型“INT”的值“假”是从未等于“null”的类型的'诠释?
如果您检查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);
}
Answer 3:
一个比较不可能是真实的,但这并不意味着它是非法的。 但是,没有,值类型可以永远是null
。
Answer 4:
不, Int32 x
永远不会成为null
。
如果你是比较一个int为空则比较运算符需要两个int?s是适用的。
“为什么用空值类型的比较是一个警告?” 本文将帮助你。
Answer 5:
值类型不能为null
,虽然也可以等于null
(考虑Nullable<>
在你的情况下, int
变量和null
被隐式转换为Nullable<Int32>
和比较。
Answer 6:
我怀疑你的特定测试只是被优化掉了编译器时,它产生IL由于测试永远是假的。
边注:这是可能有一个空Int32使用的Int32? X来代替。
Answer 7:
我想这是因为“==”是一个语法糖,实际上代表呼吁System.Object.Equals
接受法System.Object
参数。 空由ECMA规范是一种特殊类型,其是衍生自当然System.Object
。
这就是为什么有只是一个警告。
Answer 8:
[编辑:由警告变为错误,并提出了明确的运营商可空,而不是字符串的黑客行为。]
按照上述评论@ supercat的聪明建议,以下运算符重载允许您生成您的自定义值类型为null的比较错误。
通过实施该比较可空类型的版本的运营商,在比较中使用空的操作,它可以让你通过过时的属性生成错误的可空版本相匹配。
直到微软给了我们回到我们的编译器警告我这个解决办法会,感谢@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;
}
}
Answer 9:
我认为最好的答案, 为什么编译器接受这是泛型类。 考虑下面的类...
public class NullTester<T>
{
public bool IsNull(T value)
{
return (value == null);
}
}
如果编译器不接受针对比较null
值类型,那么它将基本上打破了这一类,具有连接到它的类型参数(即它只能与基于非价值工种)的隐式约束。
Answer 10:
编译器将允许您比较实施任何结构==
空。 它甚至可以让你比较一个int为空(你会得到一个警告,虽然)。
但是,如果你反汇编代码,你会看到,当代码被编译的比较被解决。 所以,举例来说,该代码(其中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
文章来源: C# okay with comparing value types to null