C#还好有比较值类型为nullC#还好有比较值类型为null(C# okay with compar

2019-05-08 21:28发布

我就遇到了这个今天不知道为什么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
标签: c# null