Click here to Skip to main content
14,695,503 members
Please Sign up or sign in to vote.
4.00/5 (1 vote)
See more:
I have a routine that I created for comparing 2 lists to see if they are equal or not. This routine started life within a button that the 2 lists were created, while I figured the solution out.

dim aList as new list(of Client)
dim bList as new list(of Client)

'
'fill the 2 lists with identical information
'

Dim Match As Boolean = False
If Not aList.Count = bList.Count Then
    'i.e. lists dont match in volume
    MsgBox(False)
Else
    Dim iPointer As Integer = 0 'list poistion marker
    For Each objectType As Client In bList
        If objectType.Equals(bList(iPointer)) = False Then
            'here if the match is false we dont need to go through the
            'rest of the list so exit the for loop and return the result
            Dim ObjectDataType As Type = objectType.GetType

            ObjectName = ObjectDataType.Name.ToString
            Match = False
            Exit For
        Else
            Match = True
        End If

        'increment the pointer variable
        iPointer += 1
   Next
End If

MsgBox(Match)


This works great when comparing 2 lists from the same function. Now trying to make the compare function more re-usable I decided to make it a function.

Function ListEquals(Of t)(ByVal list1 As List(Of t), ByVal list2 As List(Of t)) As Boolean
     Dim Match As Boolean = False

    If Not list1.Count = list2.Count Then
        'i.e. lists dont match in volume
        Return False
    Else
        Dim iPointer As Integer = 0 'list poistion marker

         For Each objectType As t In list2
            If objectType.Equals(list1(iPointer)) = False Then
                'here if the match is false we dont need to go through 
                'the rest of the list so exit the for loop and return the result
                Dim tt As Type = objectType.GetType
                ObjectName = tt.Name.ToString
                Match = False
                Exit For
             Else
                Match = True
             End If

             'increment the pointer variable
             iPointer += 1
         Next
    End If

    Return Match
End Function


and pass in the 2 identical lists to the function it always returns false, where as the first peice of code will return true.

Update - If I remove the "of t" and replace it for actual types i.e. "of client" in the function it works, what would cause t to make the Equalitable to not work?

Anyone got any pointers / ideas of what to check?

Thanks
Simon
Posted
Updated 8-Jun-11 7:34am
v2

This is probably down to the specifics of the Client class. Do the original code and the generic method actually call the same Equals method?

You could have a problem if the Client class implements value equality via IEquatable without also overriding the Object.Equals method. If this is the case then the generic method would be making a reference comparison via Object.Equals.

[Additional information]

The IEquatable interface declares a strongly typed Equals method. When used it is important to make sure that the new IEquatable.Equals method and the existing Object.Equals method both give the same result. The code example shows how that can be done and I've set it up so that Client objects are considered equal if their Identity properties return the same value.

Public Class Client
    Implements IEquatable(Of Client)

Private id As Int32

Public ReadOnly Property Identity() As Int32
    Get
        Return Me.id
    End Get
End Property

' IEquatable.Equals
Public Function Equals(other As Client) As Boolean
    ' other is null
    If ReferenceEquals(other, Nothing) Then
        Return False
    End If
    ' same object
    If ReferenceEquals(Me, other) Then
        Return True
    End If
    ' different type, other is derived from Client
    If other.GetType() <> Me.GetType() Then
        Return False
    End If
    Return Me.Identity = other.Identity
End Function


Public Overrides Function Equals(other As Object) As Boolean
      ' call IEquatable.Equals
    Return Me.Equals(TryCast(other, Client))
End Function

End Class


Private Function ListEquals(Of T As IEquatable(Of T))(list1 As List(Of T), list2 As List(Of T)) As Boolean
    ' Note: the constraint allows this method to use of IEquatable.Equals
    Dim same As Boolean = list1.Count = list2.Count
    Dim i As Int32 = 0
    While same AndAlso i < list1.Count
        same = list1(i).Equals(list2(i))
        i += 1
    End While
    Return same
End Function


The constraint on the ListEquals method specifies that T must implement IEquatable and most importantly allows the generic method to use the new Equals method.

Without the constraint the comparison method would not know that IEquatable.Equals existed and it could only use Object.Equals. With my implementation of Client this wouldn't matter as my IEquatable.Equals and my Object.Equals have been coded to give the same result.

Alan.
   
v3
Comments
Simon_Whale 8-Jun-11 13:57pm
   
If it's not a seperate comparison function the equality works as expected within the class. It only when it's moved to a generic comparison function that the equal function of the class never gets called and a incorrect return value is given
Alan N 8-Jun-11 14:13pm
   
OK, so does Client implement a custom Equals method?
Simon_Whale 8-Jun-11 15:06pm
   
Yes it does
   
Please see my solution.
--SA
Simon_Whale 9-Jun-11 4:36am
   
Alan,

Thanks for your help and time, with what you have given me has helped me understand the interfaces use more and that it wasn't the correct solution for me on comparing the 2 objects from a list even though it gave the expected answers on all test while the comparison wasn't in its own function.

I have created a solution that uses a predicate on the list find function which works better than the previous solution that I was using
First, you're doing very simple thing — comparison of lists with the same element type, so you can use only one generic parameter — this element's type. Also, you don't need to implement IEqualitable, which is generally used to compare objects of different types.

Your task is much more simple. Derive a generic type from the type System.Collections.Generic.List — it is not sealed. In the derived class, override object.Equals and define operators "!=" and "==". It will also require overridden object.GetHashCode (do I have to explain why? to keep valid use of your type in hash-based containers like Dictionary). You already know how to do the comparison itself. Only you forgot to compare list instances to null. Do it, too, otherwise your implementation is incorrect. Two null values comparison should return true.

Also, don't forget to use object.ReferencesEquals, not "==" in your implementations, by two reasons: 1) if you implement "==", you would get into infinite recursion if "==" uses "==" in its implementation; 2) if two lists are equal as references, there is no point to check up list members — list instances are already equal.

—SA
   
v3
Comments
Simon_Whale 9-Jun-11 4:37am
   
-SA

Thanks again! you have given me the pointers I need to create a working solution
   
You're welcome. I hope this is all you need.
Good luck, call again.
--SA

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900