C#可以将值类型与null进行比较

时间:2009-12-29 00:13:33

标签: c# null

我今天碰到了这个,并且不知道为什么C#编译器没有抛出错误。

Int32 x = 1;
if (x == null)
{
    Console.WriteLine("What the?");
}

我对x怎么可能是null感到困惑。特别是因为这个赋值肯定会引发编译器错误:

Int32 x = null;

是否有可能x变为null,Microsoft是否决定不将此检查放入编译器,还是完全错过?

更新:在弄乱编写本文的代码之后,编译器突然发出警告,表达式永远不会成立。现在我真的迷路了。我把对象放到一个类中,现在警告已经消失,但是留下了问题,值类型最终是否为null。

public class Test
{
    public DateTime ADate = DateTime.Now;

    public Test ()
    {
        Test test = new Test();
        if (test.ADate == null)
        {
            Console.WriteLine("What the?");
        }
    }
}

10 个答案:

答案 0 :(得分:115)

这是合法的,因为运营商重载决策有一个独特的最佳运营商可供选择。有一个==运算符,它有两个可空的整数。 int local可以转换为可以为null的int。 null文字可以转换为可以为null的int。因此,这是==运算符的合法用法,并且总是会导致错误。

同样,我们也允许你说“if(x == 12.6)”,这也总是假的。 int local可以转换为double,文字可以转换为double,显然它们永远不会相等。

答案 1 :(得分:17)

这不是错误,因为有(int?)转换;它确实在给出的示例中生成警告:

  

表达式的结果总是“假”,因为类型'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);
}

答案 2 :(得分:5)

比较永远不会成立的事实并不意味着它是非法的。尽管如此,不,值类型可以是null

答案 3 :(得分:3)

不,Int32 x不会成为null

  

如果要将int与null进行比较   然后是比较运算符   需要两个int?s适用。

"Why a comparison of a value type with null is a warning?"文章会对您有所帮助。

答案 4 :(得分:1)

值类型不能是null,尽管它可能等于null(考虑Nullable<>)。在您的情况下,int变量和null会隐式转换为Nullable<Int32>并进行比较。

答案 5 :(得分:0)

我怀疑你的特定测试只是在生成IL时由编译器进行优化,因为测试永远不会是假的。

附注:可以使用可空的Int32使用Int32吗? x而不是。

答案 6 :(得分:0)

我想这是因为“==”是一种语法糖,它实际上代表了对接受System.Object.Equals参数的System.Object方法的调用。 NMA by ECMA规范是一种特殊类型,当然源自System.Object

这就是为什么只有警告。

答案 7 :(得分:0)

[编辑:对错误发出警告,并让操作员明确了解可空的而不是字符串黑客。]

根据@ supercat在上面评论中的聪明建议,以下运算符重载允许您生成有关将自定义值类型与null进行比较的错误。

通过实现与您的类型的可空版本进行比较的运算符,在比较中使用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;
    }
}

答案 8 :(得分:0)

我认为编译器接受此为什么的最佳答案是针对泛型类。考虑以下课程......

public class NullTester<T>
{
    public bool IsNull(T value)
    {
        return (value == null);
    }
}

如果编译器没有接受对null的值类型的比较,那么它本质上会破坏这个类,其类型参数附加了一个隐式约束(即它只适用于非值基于类型)。

答案 9 :(得分:0)

编译器将允许您比较实现==的任何结构为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);

已翻译为假:

IL_001e:  ldc.i4.0