Question

See the following code:

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

How do I avoid Equals from returning something different after boxing structures? Is there some way to call the original Equals implementation?

I know I can use the = operator in this case, but EqualsNothing is supposed to work with both classes and structures. The = operator will not work with classes in VB.NET, and it will also not work with structures that not have implemented this operator. Equals does work with everything, but as I demonstrated above, Equals doesn't return the same thing after boxing.

So, how should I rewrite EqualsNothing so that it works with classes and structures?

Edit: I tried making EqualsNothing generic, but that did not help.

Was it helpful?

Solution 3

I'm not sure this will work in all cases, but you might try this generic method:

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

OTHER TIPS

I am normally a C# guy, so I used Linqpad to demonstrate this behavior with this code:

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())

Putting 0 in the object i forces the box, just like calling the method.

The results, as the question indicates are:

a=True
b=False

The IL generated is:

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         

As you can see, the difference is in what implementation of Equals is called. In the first case, the Int32.Equals (Int32 is a CLR structure) is called. This is a value equivilence check.

In the second instance, the Object.Equals is called which does a reference comparison--do the references point at the same object?

You should not expect the same behavior from these methods.

I would suggest that you need to ask yourself why are you comparing integers to Nothing? They can NEVER be Nothing, but objects can, so the system's behavior is entirely appropriate.

Again, an Integer CANNOT be Nothing. It is almost meaningless to compare it to Nothing--except that the CLR type system has a a contract that Equals has to return something.

So what you should do is reanalyze what you are doing so you are never comparing plain integers to Nothing, and never forcing the false box by the formal parameters of your procedure or function. Instead, pass them as Integer by value so boxing does not happen.


If you absolutely positively have to do this, have two overloads.

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

CLR semantics will select the non-boxed integer one preferntially.

I am writing this at 3:00 AM so not checking the VB syntax details above, since this is in response to comment discussion.

Or just force the thing to be unboxed:

Private Function EqualsNothing(ByVal item As Object) As Boolean

   Dim int As Integer = item

   Return int.Equals(Nothing)
End Function

Again, its late--syntax not checked. The risk with this is that if the object is NOT an integer, you will get an exception.

Instead of using Equals, you can use IsNothing(object)

Private Function EqualsNothing(ByVal item As Object) As Boolean
 Return IsNothing(item)     
End Function

Use the magic of typed objects to you're advantage.

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

This is simply a question of de-boxing the objects but can be extended to support any type you use. Just be smart and place this functonality in a shared object compare so its not copy pasted everywhere.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top