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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1972262/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 22:19:23  来源:igfitidea点击:

C# okay with comparing value types to null

c#null

提问by Joshua Belden

I ran into this today and have no idea why the C# compiler isn't throwing an error.

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

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

I'm confused as to how x could ever possibly be null. Especially since this assignment definitely throws a compiler error:

我很困惑 x 怎么可能为空。特别是因为这个分配肯定会引发编译器错误:

Int32 x = null;

Is it possible that x could become null, did Microsoft just decide to not put this check into the compiler, or was it missed completely?

x 是否有可能变为空,微软是否只是决定不将此检查放入编译器,还是完全错过了?

Update: After messing with the code to write this article, suddenly the compiler came up with a warning that the expression would never be true. Now I'm really lost. I put the object into a class and now the warning has gone away but left with the question, can a value type end up being null.

更新:在弄乱了写这篇文章的代码之后,编译器突然想出了一个警告,这个表达式永远不会为真。现在我真的迷路了。我将对象放入一个类中,现在警告消失了,但留下了一个问题,值类型最终是否可以为空。

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

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

采纳答案by Eric Lippert

This is legal because operator overload resolution has a unique best operator to choose. There is an == operator that takes two nullable ints. The int local is convertible to a nullable int. The null literal is convertible to a nullable int. Therefore this is a legal usage of the == operator, and will always result in false.

这是合法的,因为运算符重载解析有一个唯一的最佳运算符可供选择。有一个 == 运算符,它接受两个可为空的整数。int local 可转换为可为空的 int。null 文字可转换为可为 null 的 int。因此,这是 == 运算符的合法用法,并且总是会导致错误。

Similarly, we also allow you to say "if (x == 12.6)", which will also always be false. The int local is convertible to a double, the literal is convertible to a double, and obviously they will never be equal.

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

回答by Adam Robinson

The fact that a comparison can never be true doesn't mean that it's illegal. Nonetheless, no, a value type can ever be null.

比较永远不可能为真的这一事实并不意味着它是非法的。尽管如此,不,值类型永远可以是null.

回答by Li0liQ

No, Int32 xwon't ever become null.

不,Int32 x永远不会变成null

If you are comparing an int to null then the comparison operator that takes two int?s is applicable.

如果您将 int 与 null 进行比较,则采用两个 int?s 的比较运算符是适用的。

"Why a comparison of a value type with null is a warning?"article will help you.

“为什么将值类型与 null 进行比较是警告?” 文章会帮助你。

回答by GrayWizardx

I suspect that your particular test is just being optimized out by the compiler when it generates the IL since the test will never be false.

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

Side Note: It is possible to have a nullable Int32 use Int32? x instead.

旁注:有可能为空的 Int32 使用 Int32?x 代替。

回答by Marc Gravell

It isn't an error, as there is a (int?) conversion; it does generate a warning in the example given:

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

The result of the expression is always 'false' since a value of type 'int' is never equal to 'null' of type 'int?'

表达式的结果始终为“false”,因为“int”类型的值永远不会等于“int?”类型的“null”。

If you check the IL, you'll see that it completelyremoves the unreachable branch - it doesn't exist in a release build.

如果您检查 IL,您会看到它完全删除了无法访问的分支——它在发布版本中不存在。

Note however that it doesn'tgenerate this warning for custom structs with equality operators. It used to in 2.0, but not in the 3.0 compiler. The code is still removed (so it knows that the code is unreachable), but no warning is generated:

但是请注意,它不会为具有相等运算符的自定义结构生成此警告。它曾经在 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
    }
}

With the IL (for Main) - note everythingexcept the MyValue(1)(which could have side-effects) has been removed:

使用 IL (for 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 
}

this is basically:

这基本上是:

private static void Main()
{
    MyValue v = new MyValue(1);
}

回答by Greg

A value type cannot be null, although it could be equal to null(consider Nullable<>). In your case the intvariable and nullare implicitly cast to Nullable<Int32>and compared.

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

回答by Vitaly

I guess this is because "==" is a syntax sugar which actually represents call to System.Object.Equalsmethod that accepts System.Objectparameter. Null by ECMA specification is a special type which is of course derived from System.Object.

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

That's why there's only a warning.

这就是为什么只有警告。

回答by yoyo

[EDITED: made warnings into errors, and made operators explicit about nullable rather than the string hack.]

[已编辑:将警告变成错误,并使操作员明确表示可空而不是字符串 hack。]

As per @supercat's clever suggestion in a comment above, the following operator overloads allow you to generate an error about comparisons of your custom value type to null.

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

By implementing operators that compare to nullable versions of your type, the use of null in a comparison matches the nullable version of the operator , which lets you generate the error via the Obsolete attribute.

通过实现与您的类型的可为空版本进行比较的运算符,在比较中使用 null 与可空版本的 operator 相匹配,这使您可以通过 Obsolete 属性生成错误。

Until Microsoft gives us back our compiler warning I'm going with this workaround, thanks @supercat!

在 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;
    }
}

回答by Lee.J.Baxter

I think the best answer as to whythe compiler accepts this is for generic classes. Consider the following class...

我认为关于编译器为什么接受这一点的最佳答案是针对泛型类。考虑以下类...

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

If the compiler didn't accept comparisons against nullfor value types, then it would essentially break this class, having an implicit constraint attached to its type parameter (i.e. it would only work with non-value-based types).

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

回答by hardkoded

The compiler will allow you to compare any struct implementing the ==to null. It even allows you to compare an int to null (you would get a warning though).

编译器将允许您将实现 的任何结构==与 null进行比较。它甚至允许您将 int 与 null 进行比较(尽管您会收到警告)。

But if you disassemble the code you will see that the comparison is being solved when the code is compiled. So, for instance, this code (where Foois a struct implementing ==):

但是如果你反汇编代码,你会看到在编译代码时正在解决比较。因此,例如,此代码(其中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);
}

Generates this IL:

生成这个 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

As you can see:

如你看到的:

Console.WriteLine(new Foo() == new Foo());

Is translated to:

翻译成:

IL_0013:  call       bool test3.Program/Foo::op_Equality(valuetype test3.Program/Foo,
                                                               valuetype test3.Program/Foo)

Whereas:

然而:

Console.WriteLine(new Foo() == null);

Is translated to false:

被翻译成假:

IL_001e:  ldc.i4.0