|
-
Sep 4th, 2022, 08:01 PM
#1
List.Contains Doing Something Unexpected
I have a class with an interface, which for this I will call ISomething. This class implements IEquatable(of ISomething).
Following this example:
https://docs.microsoft.com/en-us/dot...ramework-4.7.2
I would expect that Contains will use the Equals method to evaluate whether or not a List contains a match, but that doesn't seem to be the case. In the test I was running, the class has a List (of ISomething). In the test example, this list had only one element in it, so I was able to switch between this, which uses Contains:
Code:
If mSubFilters.Contains(other.GetSubFilter(x)) = False Then
Return False
End If
and this which is just a straight comparison of the one and only elements in both lists:
Code:
If mSubFilters(0).EqualsOtherISomething(other.GetSubFilter(x)) = False Then
Return False
End If
I did change the name of the method, but that shouldn't matter, as it implements IEquatable(of ISomething).Equals, as it must to implement the interface.
The second code runs correctly, because the two are equal. They are not the same object, they are just the same type with the same values, but one is a copy of the other, so they are different references.
The link I provided suggests that the first block of code should run correctly, because it states that an object that implements Equals (actually IEquatable(of T) in the example), will use that to evaluate whether the sought item is contained in the list. The first block does not run correctly, as it says that the copy is not found in the list, even though it is there, as the second code shows.
Clearly, List.Contains is not using the Equals, but is doing something else. What am I misunderstanding?
My usual boring signature: Nothing
 
-
Sep 4th, 2022, 10:28 PM
#2
Re: List.Contains Doing Something Unexpected
Did you see this in the documentation for IEquatable(Of T)?
If you implement IEquatable<T>, you should also override the base class implementations of Equals(Object) and GetHashCode() so that their behavior is consistent with that of the Equals(T) method.
If I'm not mistaken, GetHashCode is called first and then Equals is only called if the initial results are the same. GetHashCode is supposed to be fast so it will quickly determine that two objects are not equal in most cases. Only where the hash codes are the same is the slower Equals method called. I could be wrong about that but I think it's true.
-
Sep 5th, 2022, 10:37 AM
#3
Re: List.Contains Doing Something Unexpected
Nope, I didn't see that. I'll have to take a look.
My usual boring signature: Nothing
 
-
Sep 5th, 2022, 12:00 PM
#4
Re: List.Contains Doing Something Unexpected
That turns out to be at least along the right track. Overriding GetHashCode proved to be unnecessary. In the example shown in the link, GetHashCode is overridden in a kind of weird way. That didn't work for me. They were using a Part ID as what looks like a unique identifier, so they could use that as the return from the overridden GetHashCode. I have a unique identifier in this class, as well, but it's a GUID...and I didn't want to match that for reasons that are probably invalid if I thought about them a bit more.
The base class GetHashCode is entirely sufficient for my purpose, if it works as you believe. However, overriding Equals(obj As Object) was essential. I had overlooked the fact that it was overridden in the example.
My usual boring signature: Nothing
 
-
Sep 5th, 2022, 12:19 PM
#5
Re: List.Contains Doing Something Unexpected
You almost certainly should be overriding GetHashCode as well. Whatever you're comparing in Equals for equality should be used to generate the hash code. For instance, you might do something like this:
vb.net Code:
Public Class Thing
Implements IEquatable(Of Thing)
Public Property Text As String
Public Property Number As Integer
Public Overloads Function Equals(other As Thing) As Boolean Implements IEquatable(Of Thing).Equals
Return other IsNot Nothing AndAlso
String.Equals(Text, other.Text) AndAlso
Number.Equals(other.Number)
End Function
Public Overrides Function Equals(obj As Object) As Boolean
Return Equals(TryCast(obj, Thing))
End Function
Public Overrides Function GetHashCode() As Integer
Return If(Text Is Nothing,
Number.GetHashCode(),
Text.GetHashCode() Xor Number.GetHashCode())
End Function
End Class
The Text and Number properties are used in the equality comparison and so the same properties are used in the hash code generation.
Last edited by jmcilhinney; Sep 5th, 2022 at 12:30 PM.
-
Sep 5th, 2022, 02:51 PM
#6
Re: List.Contains Doing Something Unexpected
Unfortunately, the comparison is considerably more complicated that the example you showed. I am comparing three or four fields, plus the contents of a list of some other type of object that has, itself, a list (of GUID) as a core element.
If I understand it correctly, the base hash code works just fine, for me. If the two objects are the same object, then they are equal. If they are not the same object, then use my considerably more complicated Equals method that does the comparison.
I might be able to convince myself that comparing the hashes of the unique identifier field is sufficient, but I might decide that I want an 'equivalent to' method in addition to an Equals, which compares everything OTHER than the unique identifier field.
My usual boring signature: Nothing
 
-
Sep 5th, 2022, 08:41 PM
#7
Re: List.Contains Doing Something Unexpected
 Originally Posted by Shaggy Hiker
If I understand it correctly, the base hash code works just fine, for me. If the two objects are the same object, then they are equal. If they are not the same object, then use my considerably more complicated Equals method that does the comparison.
I don't think that's right. I suggest that you read this. This part seems to contradict your understanding:
Two objects that are equal return hash codes that are equal. However, the reverse is not true: equal hash codes do not imply object equality, because different (unequal) objects can have identical hash codes.
You seem to be suggesting that, in your case, two different objects that are equal may not - quite possibly cannot - produce the same hash code.
-
Sep 6th, 2022, 09:46 AM
#8
Re: List.Contains Doing Something Unexpected
There is a certain fuzziness in my language, which is because there is a certain fuzziness in the situation. In some classes, I deliberately created a method IsEquivalentTo rather than Equals, because they aren't equal...but they don't need to be for the way they must be used. In the specific classes that prompted me to start this thread, I can't quite decide whether or not they have to be strictly equal.
Most of these classes will be formed from querying a pair of DB tables. These tables will each have a primary key, which will be part of the class. However, I haven't yet settled on whether or not class A has to have the same PK as class B for A=B to be true. The class represents a concept. It's a certain type of filter. Two filters can filter for the same thing in the same way, in which case they can be equal, but do they have to have the same PK in the DB? That I have not yet decided.
Though, as I write this, I just realized that they don't need the PK at all, and probably shouldn't have it, since there's a different field that would make more sense as the PK, which makes the whole question a bit moot...It's an evolving design.
My usual boring signature: Nothing
 
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|