在VB.NET中,“foo = Nothing”和“foo is Nothing”的区别是什么?

10

VB.NET 中,下列两种定义方式有何不同:

if foo is Nothing Then
      doStuff()
End If

if foo=Nothing Then
    doStuff()
End If

更新我收到了以下回答:

foo is Nothing 简单地检查 foo 是否未分配给任何引用。 foo = Nothing 检查由 foo 持有的引用是否等于 nothing

运行这三个语句后,

Dim foo as Object
Dim bar as Integer
foo = bar

foo is Nothing 求值为假,foo = Nothing 求值为真。

但是,如果将 bar 声明为一个未初始化的 Object,那么 foo is Nothingfoo = Nothing 都会求值为真!我认为这是因为 Integer 是值类型而 Object 是引用类型导致的。

6个回答

11

这取决于类型。

  • 对于值类型Is不起作用,只能使用=Nothing是指该类型的默认实例(即通过调用给定类型TNew T()来获得的实例)。

  • 对于引用类型Is执行引用比较(与object.ReferenceEquals(a, Nothing)相同)。 a = Nothing通常不起作用,除非Operator=已明确为该类定义。

    此外,如果Operator=已正确实现,则foo = Nothingfoo Is Nothing应产生相同的结果(但对于Nothing之外的任何其他值都不成立),但是foo Is Nothing将更有效,因为它是编译器 内置,而Operator=将调用方法。

  • 对于可空值类型(即Nullable(Of T)的实例),适用特殊规则:像所有其他运算符一样,=由编译器提升(注意该博客文章中的错误…)到基础类型。因此,比较两个Nullable的结果不是Boolean,而是Boolean?(注意?)。但是,由于所谓的“null传播”适用于提升的运算符,这将始终返回Nothing,无论foo的值如何。引用Visual Basic 10语言规范(§1.86.3):

    如果任一操作数为Nothing,则表达式的结果是一个以空值版本的结果类型键入的Nothing值。

    因此,如果用户要将Nullable变量与Nothing进行比较,则必须使用foo Is Nothing语法,对此编译器生成特殊代码使其正常工作(Visual Basic 10语言规范的§1.79.3)。 感谢Jonathan Allen坚持我的错误;感谢Jared Parsons向我传递了Visual Basic 10规范的链接。

(以上假设使用Option Strict On,您应该始终这样做。如果不是这种情况,由于调用foo = Nothing可能执行后期绑定调用,结果会略有不同。)

对于字符串 = 和 Is 产生不同的结果,我敢说 = 对于字符串实现了最小惊奇原则,因此我认为它是正确的。 - Lasse V. Karlsen
@Lasse:是的,在VB中,字符串是一个特殊情况,因为=会调用一个特殊的方法而不是Equals,这个方法将把Nothing视为空字符串。 - Konrad Rudolph
1
你忘记了 Nullable(Of T)。因此,foo = Nothing 会编译通过,但这会得到错误的答案。 - Jonathan Allen
@Jonathan:Nullable(Of T)是一个值类型,我的回答仍然适用。 foo = Nothing 给出正确的答案,并且它等同于 Not foo.HasValue - Konrad Rudolph
@Jonathan:那与此无关。编译器在这里发出警告,因为If是多余的:当然,foo将始终为Nothing,因为它已经被初始化,并且从未更改过。编译器足够聪明,可以弄清楚这一点,但这与可空类型的可比性无关。如果你不相信我,只需尝试Dim foo As Integer? = 1Dim foo As Integer? = If(New Random().NextDouble() < 0.5, 1, CType(Nothing, Integer?)) - 这模拟了一个抛硬币的过程,所以编译器无法猜测foo的值。 - Konrad Rudolph
显示剩余5条评论

3
foo is Nothing simply checks if `foo` is not assigned to any reference.

foo=Nothing checks if the reference held by `foo` is equal to `nothing`

在VB中,如果foo未初始化,则两个语句将评估为相同的值。

如果foo不是引用类型呢?或者foo是一个字符串?你确定它会评估为相同的值吗? - Lasse V. Karlsen
当方法是泛型时,值类型就会发挥作用,但对于空字符串(即“”),Is和=将产生不同的结果。 - Lasse V. Karlsen
@Lasse,当foo是Nullable(Of T)时,请参见我的答案。 - Jonathan Allen

2
这里是一些用于验证差异的IL代码:
.method public static void Main() cil managed
{
    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
    .entrypoint
    .maxstack 3
    .locals init (
        [0] object o,
        [1] bool VB$CG$t_bool$S0)
    L_0000: nop 
    L_0001: newobj instance void [mscorlib]System.Object::.ctor()
    L_0006: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
    L_000b: stloc.0 
    L_000c: ldloc.0 
    L_000d: ldnull 
    L_000e: ceq 
    L_0010: stloc.1 
    L_0011: ldloc.1 
    L_0012: brfalse.s L_001f
    L_0014: ldstr "Is Nothing"
    L_0019: call void [mscorlib]System.Console::WriteLine(string)
    L_001e: nop 
    L_001f: nop 
    L_0020: ldloc.0 
    L_0021: ldnull 
    L_0022: ldc.i4.0 
    L_0023: call bool [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Operators::ConditionalCompareObjectEqual(object, object, bool)
    L_0028: stloc.1 
    L_0029: ldloc.1 
    L_002a: brfalse.s L_0037
    L_002c: ldstr "Is nothing"
    L_0031: call void [mscorlib]System.Console::WriteLine(string)
    L_0036: nop 
    L_0037: nop 
    L_0038: nop 
    L_0039: ret 
}

VB代码:

Sub Main()
        Dim o As New Object

        If o Is Nothing Then
            Console.WriteLine("Is Nothing")
        End If

        If o = Nothing Then
            Console.WriteLine("Is nothing")
        End If
    End Sub

也尝试使用包含“Nothing”的字符串、包含“”的字符串和一个值类型(比如整数),并使用该值类型的默认值和其他值,例如将其保留为0,然后也检查1。 - Lasse V. Karlsen
@Lasse - 抱歉我没有时间再添加更多的例子了。也许其他人可以为参考做这件事? - Jason Evans

1

foo是指向内存位置的指针,Nothing表示“不指向任何内存,因为尚未分配任何内存”。等于意味着当您比较2个值类型时,它们具有相同的值。但是,您假设foo表示一个对象,这始终是一个引用类型,旨在指向内存中的对象。'is'用于比较对象类型,并且仅在您有两个指向相同值的对象时才会返回'true'。

假设您有一个名为clsFoo的类,其中包含一个公共整数成员变量“x”,foo1和foo2都是clsFoo,y和z是整数。

foo1=new clsFoo
foo2=new clsFoo
foo1.x=1
foo2.x=1
y=2
z=1
dim b as boolean 

b= foo1 is not foo2  ' b is true
b= foo1.x=foo2.x ' b is tree
b= foo1 is foo2 'b is false  
b= foo1.x=z ' true of course
foo2.x=3
b= foo1.x=foo2.x ' false of course
foo1=foo2
b=foo1 is foo2 ' now it's true
b= foo1.x=foo2.x ' true again
b= 3=3 ' just as this would be
b= foo1=foo2 ' ERROR: Option Strict On disallows operands of type Object for operator '='. Use the 'Is' operator to test for object identity.

永远不要忘记打开严格选项。如果不这样做,就等于在大喊“请让我的程序烂透了”。


看起来你忘记了可空结构,应该使用 Is Nothing 而不是 = Nothing - Jonathan Allen
OP的问题并没有明确地请求这些细节,除非他把它当作一个竞赛来提出。;-) - FastAl

1

这取决于Foo的类型。

引用类型

if foo = Nothing then 'This depends on how the op_Equals operator is defined for Foo. If not defined, then this is a compiler error. 
if foo Is Nothing then 'Evaluates to True is foo is NULL

值类型

if foo = Nothing then 'Evaluates to True is foo has the default value in every field. For most types the default is 0.
if foo Is Nothing then 'Compiler Error

可空值类型

if foo = Nothing then 'This always evaluates to false. In VB 10, this is a compiler warning
if foo Is Nothing then 'Evaluates to True is foo.HasValue = False

很多人不理解VB中的空值传播。与SQL类似,它使用三值逻辑,因此“a=b”的答案可能为True、False或Null。在If语句中,Null被视为False。 警告:你不能只写If Not(Foo = Nothing) Then,因为“Not(Nothing)”仍然是“Nothing”。

1

假设:

MyFunc(Foo作为对象)

如果Foo是ValueType,则将其装箱

如果foo为Nothing,则

object.ReferenceEquals(代码内联-最快的方法)

如果foo = Nothing,则

Operators.ConditionalCompareObjectEqual(foo,Nothing,False)

Vb.Net像Obj1 = Obj2这样处理此情况。它不使用Obj.equals(obj2)!如果Obj1为nothing,则会出现异常

此选项使用非常复杂的代码,因为有很多选项取决于所有可能的foo定义。

尝试这个:

Sub Main()
  Dim o As Object = 0
  Debug.Print(o Is Nothing)  'False
  Debug.Print(o = Nothing)   'True 
End Sub

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接