Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable.Equals seems to call the wrong Equals method

I implemented a PagedModel class to wrap around IEnumerable to provide paging data for grids in my MVC app. I used Resharper's auto-generated Equality code telling it to check the data, total rows, page number, and page size fields. Here's the class code:

Public Class PagedModel(Of T)
    Public Property PageSize As Integer
    Public Property PageNumber As Integer
    Public Property ModelData As IEnumerable(Of T)
    Public Property TotalRecords As Integer

    Public Overloads Function Equals(ByVal other As PagedModel(Of T)) As Boolean
        If ReferenceEquals(Nothing, other) Then Return False
        If ReferenceEquals(Me, other) Then Return True
        Return other._PageSize = _PageSize AndAlso other._PageNumber = _PageNumber AndAlso Equals(other._ModelData, _ModelData) AndAlso other._TotalRecords = _TotalRecords
    End Function

    Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean
        If ReferenceEquals(Nothing, obj) Then Return False
        If ReferenceEquals(Me, obj) Then Return True
        If Not Equals(obj.GetType(), GetType(PagedModel(Of T))) Then Return False
        Return Equals(DirectCast(obj, PagedModel(Of T)))
    End Function

    Public Overrides Function GetHashCode() As Integer
        Dim hashCode As Long = _PageSize
        hashCode = CInt((hashCode * 397) Xor _PageNumber Mod Integer.MaxValue)
        If _ModelData IsNot Nothing Then hashCode = CInt(((hashCode * 397) Xor _ModelData.GetHashCode()) Mod Integer.MaxValue)
        hashCode = CInt((hashCode * 397) Xor _TotalRecords Mod Integer.MaxValue)
        Return CInt(hashCode Mod Integer.MaxValue)
    End Function
End Class

I found the call to Equals(other._ModelData, _ModelData) peculiar, as AFAIK, this checks that it is the same object rather than that the contained items are the same. Because my tests were failing anyways, I went ahead and changed it to other._ModelData.Equals(_ModelData) with no success. Then I reflected into it at debug time and found that other._ModelData.GetType().GetMethod("Equals",{GetType(Object)}).DeclaringType was Object! Obviously, that would result in the failed comparison.

I came up with a solution to create a EnumerableEquals method which compares every item in the two enumerables to confirm the are the same, but it seems sloppy. Is there anything I can do to use the normal .Equals method?

Private Function EnumerableAreEqual(ByVal a As IEnumerable(Of T), ByVal b As IEnumerable(Of T)) As Boolean
    b = b.ToList() 'avoid multiple query execution
    Return a.All(Function(item) b.Contains(item))
End Function
like image 614
just.another.programmer Avatar asked May 23 '12 20:05

just.another.programmer


1 Answers

You probably want to use SequenceEqual.

(new[]{1,2,3}).SequenceEqual(new[]{1,2,3}) // True
(new[]{1,2,3}).SequenceEqual(new[]{3,2,1}) // False
(new[]{1,2,3}).SequenceEqual(new[]{1,2})  // False
(new[]{1,2}).SequenceEqual(new[]{1,2,3})  // False

This will ensure that both IEnumerables have the same elements in the same order.

like image 173
StriplingWarrior Avatar answered Sep 22 '22 21:09

StriplingWarrior