Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/xpath/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#可以将值类型与null进行比较_C#_Null - Fatal编程技术网

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

C#可以将值类型与null进行比较,c#,null,C#,Null,我今天遇到了这个问题,不知道为什么C#编译器没有抛出错误 Int32 x = 1; if (x == null) { Console.WriteLine("What the?"); } 我很困惑x怎么可能是空的。特别是由于此赋值肯定会引发编译器错误: Int32 x = null; 是否有可能x变为空,是微软决定不把这个检查放进编译器,还是它完全丢失了 更新:在弄乱了写这篇文章的代码之后,编译器突然提出了一个警告,这个表达式永远不会是真的。现在我真的迷路了。我将对象放入一个类中,现在

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

Int32 x = 1;
if (x == null)
{
    Console.WriteLine("What the?");
}
我很困惑x怎么可能是空的。特别是由于此赋值肯定会引发编译器错误:

Int32 x = null;
是否有可能x变为空,是微软决定不把这个检查放进编译器,还是它完全丢失了

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

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

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

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

不,
Int32 x
永远不会变成
null

如果将int与null进行比较 然后比较运算符 取两个整数即可


文章将对您有所帮助。

我怀疑编译器在生成IL时正在优化您的特定测试,因为测试永远不会出错


旁注:可以有一个可为空的Int32使用Int32吗?这不是一个错误,因为存在(
int?
)转换;在给出的示例中,它确实会生成警告:

表达式的结果始终为“false”,因为“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);
}

值类型不能为
null
,尽管它可以等于
null
(请考虑
null
)。在您的例子中,
int
变量和
null
被隐式转换为
Nullable
,并进行比较。

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


这就是为什么只有一个警告。

这是合法的,因为运算符重载解析有一个唯一的最佳运算符可供选择。有一个==运算符接受两个可为null的整数。int local可转换为可为null的int。null literal可转换为可为null的int。因此,这是==运算符的合法用法,并且将始终导致false

类似地,我们也允许您说“if(x==12.6)”,这也总是错误的。int local可转换为double,literal可转换为double,显然它们永远不会相等。

[编辑:将警告变成错误,并使运算符明确表示可为null,而不是字符串hack。]

根据@supercat在上述注释中的巧妙建议,以下运算符重载允许您生成一个关于将自定义值类型与null进行比较的错误

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

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

我认为编译器接受这一点的最佳答案是泛型类。考虑下面的类…

public class NullTester<T>
{
    public bool IsNull(T value)
    {
        return (value == null);
    }
}
公共类NullTester
{
公共布尔值为空(T值)
{
返回值(值==null);
}
}

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

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

但是,如果您反汇编代码,您将看到在编译代码时正在解决比较问题。 例如,这个代码(其中
Foo
是一个实现
==
的结构):

生成此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

如果(1==2)也可以编写
。执行代码路径分析不是编译器的工作;这就是静态分析工具和单元测试的用途;不,它不可能是空的。同意(1==2),我更想知道(1==null)的情况,谢谢大家的回复。关于警告或无警告问题:如果所讨论的结构是所谓的“简单类型”,如
int
,编译器会生成很好的警告。对于简单类型,
=
运算符由C语言规范定义。对于其他(非简单类型)结构,编译器忘记发出警告。有关详细信息,请参阅。对于非简单类型的结构,
=
运算符必须由作为结构成员的
opeartor==
方法重载(否则不允许使用
=
)。最近也有人在内部向我报告了这一点。我不知道我们为什么停止发出这种警告。我们把它作为一个bug输入。给你:但是一个值类型可以
IL_001e:  ldc.i4.0