我通常是一个 C# 人,所以我使用 Linqpad 用这段代码演示了这种行为:
dim a as object
dim b as object
dim i as object
a = 0.Equals(Nothing)
Console.WriteLine("a={0}", a.ToString())
i = 0
b = i.Equals(Nothing)
Console.WriteLine("b={0}", b.ToString())
将 0 放入对象 i 会强制框,就像调用方法一样。
结果,如问题所示:
a=True
b=False
生成的 IL 为:
IL_0001: ldc.i4.0
IL_0002: stloc.3
IL_0003: ldloca.s 03
IL_0005: ldc.i4.0
IL_0006: call System.Int32.Equals
IL_000B: box System.Boolean
IL_0010: stloc.0
IL_0011: ldstr "a={0}"
IL_0016: ldloc.0
IL_0017: callvirt System.Object.ToString
IL_001C: call System.Console.WriteLine
IL_0021: nop
IL_0022: ldc.i4.0
IL_0023: box System.Int32
IL_0028: stloc.2
IL_0029: ldloc.2
IL_002A: ldnull
IL_002B: callvirt System.Object.Equals
IL_0030: box System.Boolean
IL_0035: stloc.1
IL_0036: ldstr "b={0}"
IL_003B: ldloc.1
IL_003C: callvirt System.Object.ToString
IL_0041: call System.Console.WriteLine
IL_0046: nop
如您所见,区别在于调用 Equals 的实现方式。在第一种情况下,调用 Int32.Equals(Int32 是一个 CLR 结构)。这是一个值等价检查。
在第二种情况下,调用 Object.Equals 进行引用比较——引用是否指向同一个对象?
您不应期望这些方法具有相同的行为。
我建议您需要问自己为什么要将整数与 Nothing 进行比较?它们永远不会是 Nothing,但对象可以,因此系统的行为是完全合适的。
同样,整数不能是空的。将它与 Nothing 进行比较几乎毫无意义——除了 CLR 类型系统有一个 Equals 必须返回某些东西的协定。
所以你应该做的是重新分析你正在做的事情,这样你就永远不会将纯整数与 Nothing 进行比较,也永远不会通过过程或函数的形式参数强制错误框。相反,将它们作为 Integer 按值传递,这样就不会发生装箱。
如果您绝对必须这样做,请进行两次重载。
Private Function EqualsNothing(ByVal item As Integer) As Boolean
Private Function EqualsNothing(ByVal item As Object) As Boolean
CLR 语义将优先选择未装箱的整数。
我在凌晨 3:00 写这篇文章,所以没有检查上面的 VB 语法细节,因为这是对评论讨论的回应。
或者只是强制拆箱:
Private Function EqualsNothing(ByVal item As Object) As Boolean
Dim int As Integer = item
Return int.Equals(Nothing)
End Function
再一次,它的late-- 语法没有被检查。这样做的风险是,如果对象不是整数,你会得到一个异常。