I have a container class similar to the one below (with much of the logic omitted):
class Container<T>
{
Dictionary<T, TWrapped> contains = new Dictionary<T, TWrapper>();
public void Add(T item)
{
TWrapped wrappedItem = new TWrapped(item);
contains[item] = wrappedItem;
// logic involving wrappedItem...
}
public bool Contains(T item)
{
return contains.ContainsKey(item);
}
// Item has been updated, so Sort called on container
public void Sort(T item)
{
TWrapped wrappedItem;
if(contains.TryGetValue(item, out wrappedItem))
{
// sort the wrappedItem within the container...
}
}
}
Is there some way of either telling users of Container that T will be used as a dictionary key or, equivalently, of forcing users to have overridden GetHashCode() and Equals() ?
Further Info
My actual container class implements a heap implicitly with an array. To do so, items are wrapped with a class that contains index information for finding parents and children within the array. This wrapper class is private within the heap to avoid unintended tampering with the index values.
I now wish to support re-sorting of specific items in the heap; however, to map from the user supplied item to its wrapped representation in the array, I need a dictionary to store such a mapping… hence the GetHashCode() Equals() woes.
Any criticisms of the design would be welcome!
Best Answer
When it comes to reference types (classes), you're already ok since those have
Equals
andGetHashCode
implemented properly even without overriding.When it comes to value types (structs, enums, ...), these should always override
Equals
andGetHashCode
in meaningful manner as well as be immutable as per MSDN design guidelines - struct should implementIEquatable<T>;
and transitivelyIEquatable<T>
should overrideGetHashCode
andEquals
(Notes to implementors part).If you want to really express this requirement to the consumer, consider adding a following generic constraint:
This will most probably only cause nuisance to consumers however, as they will be forced to needlessly implement
IEquatable<T>
on classes.Another option is to force every consumer to supply an
IEqualityComparer<T>
to yourContainer<T>
which will then be used by theDictionary
:Presumably, most consumers will do this
but it feels nice to somehow mirror the
Dictionary
's ctors for increased flexibility. If I wanted ordinal case-insensitive string keys, I could do