Use case of IEqualityComparer<TKey> on generic dictionary
Today while researching for a project I was looking through the docs for Dictionary<TKey, TValue>
. There I found some remark I may have overlooked in the past:
Because keys can be inherited and their behavior changed, their absolute uniqueness cannot be guaranteed by comparisons using the Equals method.
This same remark seems to be sprinkled all over the docs for most (keyed) collection classes I looked at afterwards. But I wasn't able to find any examples that illustrate the potential problem described, alt开发者_运维技巧hough I'm quite sure that this is what the constructors accepting an IEqualityComparer<TKey>
are for, and that it arises when complex types are being used as keys.
Can anyone explain to me under which circumstances one might run into a problem this comment points at? As long as I don't know exactly how or when such a problem may arise, I can't design to allow for it.
Using any object/value as a key is not as trivial as it sounds, as it depends on a correct implementation of Equals and GetHashCode. To be honest, the "subclass distorting things" scenario is not one I've hugely encountered, however a more common use of this API is to allow usage when the type you are using as the key doesn't provide a suitable Equals/GetHashCode, and is outside of your control. For example, you want to use Customer
as a key, but you only want to match on the Id
(i.e. you don't want the default reference equality behaviour; maybe you are handling different objects from different sources, and need different instances to be considered equal based on the Id
). In this scenario you could write a custom equality comparer that considers only the Id
.
Personally, though; I'm a fan of simple. I very rarely (if ever) index on anything except int
, string
, etc. This keeps things simple - and at the same time shows another common scenario: providing a case-sensitive/insensitive or culture-sensitive/insensitive comparer for a string
key.
Takrl,
Say a you're processing a big query result on a server: you're building a big Dictionary<Student, List<Result>>
... and meanwhile someone-else mutates the identity of a Student object in the shared-model underneath you (say someone changes "Bob"s first name to "Fred"). If the Name attribute contributes to Students' Equals (and HashCode) method then the next time you try to add a Result for "Bob" to your Dictionary, you won't FIND the object-which-represents-Bob in the Dictionary, simply because the "Bob" object's HashCode has changed... so his existing results are "lost".
Hence: One of the tricks with using any class as-a-key is to make it's Identity fields (those used in equals and HashCode methods) immutable.
In practice, this won't matter until you're accessing your model from multiple threads concurrently. In a single-threaded environment you really don't have to worry, simply because YOU won't be silly enough to mutate an Identity you need WHILE you need it.
Does that make sense?
Cheers. Keith.
精彩评论