Reputation: 8785
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
Upvotes: 5
Views: 838
Reputation: 81179
In general, for any two arbitrary storage locations X
and Y
, the value of X.Equals(Y)
should never change unless X
or Y
is written. While it would be possible for an immutable collection type to override Equals
to test sequence equality, mutable class types cannot sensibly do anything with Equals(Object)
other than test for reference identity (which is the default behavior).
Upvotes: 0
Reputation: 87238
You can't really use the "normal" Equals method, since it's not defined for enumerables (i.e., it doesn't do element comparison). What you have is perfectly fine (*), but if you want to use the Equals
syntax, you can consider using the what @StriplingWarrior suggested.
(*) Your implementation doesn't really check whether both are equal. If 'a' has more elements than 'b', it will return True; also, if 'a' has the same elements as 'b', but in a different order, it will also return True. If that's ok for your scenario, then it's fine.
Upvotes: 0
Reputation: 156544
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.
Upvotes: 5