装箱后,等于(......)不会返回相同的值

时间:2012-11-23 00:46:48

标签: .net vb.net comparison equals value-type

请参阅以下代码:

Private Function EqualsNothing(ByVal item As Object) As Boolean
  Return item.Equals(Nothing)
End Function

Console.WriteLine(0.Equals(Nothing)) ' True
Console.WriteLine(EqualsNothing(0)) ' False

如何在拳击结构后避免Equals返回不同的东西?有没有办法调用原始Equals实现?

我知道在这种情况下我可以使用=运算符,但EqualsNothing应该适用于类和结构。 =运算符不能用于VB.NET中的类,并且它也不适用于没有实现此运算符的结构。 Equals 可以使用所有内容,但正如我在上面演示的那样,Equals在装箱后不会返回相同的内容。

那么,我应该如何重写EqualsNothing以便它适用于类和结构?

修改:我尝试将EqualsNothing设为通用,但这没有帮助。

4 个答案:

答案 0 :(得分:5)

我通常是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强制该框,就像调用方法一样。

结果如问题所示:

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进行比较几乎没有意义 - 除了CLR类型系统有一个Equals必须返回的合同。

所以你应该做的是重新分析你正在做的事情,这样你就永远不会将普通整数与Nothing进行比较,也绝不会通过程序或函数的形式参数强制使用假框。相反,按值将它们作为整数传递,这样就不会发生拳击。


如果你绝对必须这样做,那就有两个重载。

Private Function EqualsNothing(ByVal item As Integer) As Boolean
Private Function EqualsNothing(ByVal item As Object) As Boolean

CLR语义将优先选择非盒装整数。

我是在凌晨3点写的,所以不要检查上面的VB语法细节,因为这是对评论讨论的回应。

或者只是强制取消装箱:

Private Function EqualsNothing(ByVal item As Object) As Boolean

   Dim int As Integer = item

   Return int.Equals(Nothing)
End Function

同样,它的晚期语法没有被检查。这样做的风险在于,如果对象不是整数,则会出现异常。

答案 1 :(得分:3)

您可以使用IsNothing(object)

,而不是使用Equals
Private Function EqualsNothing(ByVal item As Object) As Boolean
 Return IsNothing(item)     
End Function

答案 2 :(得分:1)

我不确定这会在所有情况下都有效,但您可以尝试这种通用方法:

Sub Main
    Console.WriteLine(0.Equals(Nothing)) ' True
    Console.WriteLine(EqualsNothing(0)) 'True
End Sub

Private Function EqualsNothing(Of T)(ByVal item As T) As Boolean
  Dim i As T = Nothing
  Return (item.Equals(i))
End Function

答案 3 :(得分:0)

使用类型对象的魔力对你有利。

public function Compare(objLeft as Object,objRight as Object) as integer
    If TypeOf (objLeft) Is Integer Then
        return Cint(objLeft) = Cint(objRight)            
    ElseIf TypeOf (objLeft) Is DateTime Then
        'Your implementation here
    ElseIf TypeOf (objLeft) Is String Then
        'Your implementation here
    End If
end function

这只是解除对象的问题,但可以扩展为支持您使用的任何类型。只要聪明一点,将这个功能放在一个共享的对象比较中,这样它就不会粘贴在任何地方。