C#: Using ContainsKey where the keys are objects - c#

Ok so I need a bit of help. I have a generic dictionary "cashdata". The keys of this dictionary are objects (Query objects, a class I have defined). Query objects have "Terms" field, which is a list of strings, and an "Operator" field, which is an enum (Either "All" or "Any").
cashdata.ContainsKey(a_query_object);
And have it yield true or false depending on if a_query_object and an object in the dictionary are identical in terms of their Terms and Operator. What is the best way to do this? A HashCode possibly? I would appreciate an example, thanks in advance.
EDIT: cashdata Dictionary is defined as such
Dictionary<Query,List<string> > cashData = new Dictionary<Query,List<string>>();

Make your object implement IEquatable Interface along with overriding Object.Equals and GetHashCode as mentioned in the remarks section in MSDN
If you implement IEquatable, you should also override the base
class implementations of Object.Equals(Object) and GetHashCode so that
their behavior is consistent with that of the IEquatable.Equals
method. If you do override Object.Equals(Object), your overridden
implementation is also called in calls to the static
Equals(System.Object, System.Object) method on your class. In
addition, you should overload the op_Equality and op_Inequality
operators. This ensures that all tests for equality return consistent
results.

Related

C# Generic Collections, IComparer, IComparable and IEquatable

I am working on a C# project where I make heavy use of interfaces, and the System.Collections.Immutable library. I wish to sort implementations of one of my interfaces in an immutable set, ImmutableSortedSet<IMyInterface>.
In Java this is a straightforward matter of implementing Comparable<IMyInterface> and overriding the equals and hash code functions. I found a similar interface in .net IComparable<IMyInterface> but it warns implementers that if they choose to implement the interface, then they should also override the comparison operators (<,>,<=,>=), as well as implement IEquatable<IMyInterface>. IEquatable<T> warns implementers that they should override the equals and hash code functions, as well as the '==' and '!=' operators.
Now I'm having second thoughts about implementing IComparable<T>, I'm not creating a new primitive type here, I just want to provide a convenient sorting algorithm for a complex reference type. Furthermore, there seems to be a certain problem in C# with overriding operators at the interface level, I am therefore leaning towards using a separate IComparer<IMyInterface> implementation.
What really raised my eyebrows though was hearing this:
The IEquatable<T> interface is used by generic collection objects such as Dictionary<TKey, TValue>, List<T>, and LinkedList<T> when testing for equality in such methods as Contains, IndexOf, LastIndexOf, and Remove. It should be implemented for any object that might be stored in a generic collection.
Does this combined with
If you implement IEquatable<T>, you should also override the base class implementations of Object.Equals(Object) and GetHashCode so that their behavior is consistent with that of the IEquatable<T>.Equals method. If you do override Object.Equals(Object), your overridden implementation is also called in calls to the static Equals(System.Object, System.Object) method on your class. In addition, you should overload the op_Equality and op_Inequality operators. This ensures that all tests for equality return consistent results.
Mean that I am expected to override both '==' and '!=' operators for any type that I want to store inside a generic collection??
IComparable<T> is the preferred mechanism to provide comparison support for sorting. The advice of implementing the comparison operators doesn't make a lot of sense for most types, and they could not be utilized by generic collections anyway. You should also implement IEquatable<T>, override GetHashCode(), and override object.Equals to delegate to IEquatable<T>.Equals.
In general, whenever I implement IComparable<T>, I also implement the non-generic IComparable, but I implement IComparable.CompareTo explicitly such that it is normally hidden.
As you already noticed, if you need sorting, you can pass IComparer<T> implementation and your type doesn't have to implement IComparable<T>. In some cases it's easier and in some cases it's the only way to go, e.g. when you want the same type to be sorted differently in different situations. You can just pass different IComparer<T> implementations in that case.
About IEquatable<T> related quotes. For most situations overriding Equals and GetHashCode is enough. It will make your type work with List<T>.Contains and similar methods. It will also allow you to use your type as key in Dictionary<TKey, TValue> and store it in HashSet<T>. That's because all these cases use EqualityComparer<T>.Default when comparer is not specified.
The way EqualityComparer<T>.Default works can be found here. As you can see it verifies that your type implements IEquatable<T>, but if it doesn't creates an instance of ObjectEqualityComparer<T>, which will just use Equals and GetHashCode methods to verify equality.

Dictionary of a class

I have a dictionary
public static Dictionary<SymbolAndStrategy, int> allPositions
SymbolAndStrategy is a class containing 2 different class, Class Symbol and Class Strategy. But when I tried to use it like as this,
allPositions.Add(new SymbolAndStrategy(StrategyName.Apple, this.ProductKey), 0);
allPositions[new SymbolAndStrategy(StrategyName.Apple, this.ProductKey)]++;
the compiler will complain "key not found" even the 2 have the SymbolAndStrategy key.
The complain of the compailer is probabbly derives from the fact that you didn't override GetHashCode() and Equal() methods of your type in order to make equal 2 different instances by their content.
In short
new SymbolAndStrategy(StrategyName.Apple, this.ProductKey) create a new instance, a new allocation. In order to "explain" to the framework that those 2 instances are equal (the one present in the dictionary as a key, and another you use for a query) you need to override those 2 methods mentioned above accordingly inside SymbolAndStrategy type.
More on that Implementing the Equals Method
That's the expected behaviour because reference types are compared by reference. Even though your objects have the content they are not treated as equal because they have different references. If you don't want this you need to override Equals and GetHashCode method in your class.
Or you can implement an IEqualityComparer for your class and pass it to Dictionary constructor, so it will be used instead of the default comparison.
The issue is the fact that, by default, Dictionary<TKey, TValue> will use Object.Equals to test for key equality. If you didn't override Equals and GetHashCode in your class, then Object.Equals will use reference equality. Since you're creating two instances of your key (albeit with the same values), Object.Equals will return false.
There are two solutions.
Override Equals and GetHashCode in your class.
Use the Dictionary<TKey, TValue> constructor that takes an IEqualityComparer<TKey>. That will allow you to implement an equality check without having to modify the original class if you don't have the ability.

Why there is no corresponding delegate for IEqualityComparer as there is a Comparison for IComparer

For those extension methods that receive an IComparer as argument, I can easily obtain the corresponding instance trough the method Comparer.Create that creates an IComparer<T> from a Comparison<T>.
In the same way, it would be nice to have a corresponding delegate for the interface IEqualityComparer. Why it does not exist?
The general contract for equality is that it must behave in a way consistent with the same hash code method: that two objects which equality says are equal must report the same hash value. So you cannot provide a consistent equality implementation through a unique method.
Instead, you require a contract that includes both methods (equals and hash code) and therefore a delegate is not enough to specify that contract and you need an interface such as IEqualityComparer including two methods.
You can find more reasons in the following answer, which uses the Distinct method as an example.

Is it important to override Equals if I'm implementing IEquatable<T>?

I know the importance of overriding GetHashCode when implementing custom equality checks - for which I have implemented IEquality<T> interface, and also the difference between generic and non-generic Equals as discussed here. Now is there a point to override Equals(object t)? Wouldn't everything come under generic Equals(T t)?
public override int GetHashCode() //required for hashsets and dictionaries
{
return Id;
}
public bool Equals(T other) //IEquatable<T> here
{
return Id == other.Id;
}
public override bool Equals(object obj) //required??
{
return Equals(obj as T);
}
Unsealed types should not implement IEquatable<T>, since the only way to ensure (or even make it likely) that derived types will implement it correctly is to implement IEquatable<T>.Equals(T) so as to call Object.Equals(Object). Since whole purpose of IEquatable<T> is to avoid wasting CPU time converting things to Object before comparing them, an implementation which calls Object.Equals(Object) couldn't do anything faster than the default behavior that would be achieved if the interface wasn't implemented.
Sealed class types may achieve a slight performance advantage by implementing IEquatable<T>; the preferred style is to have Object.Equals(Object) try to cast the parameter to T; if the cast succeeds, use the IEquatable<T>.Equals implementation; otherwise return false. In no case should IEquatable.Equals(T) and Object.Equals(Object) yield different results when passed the same object instance.
Structure types may use the same pattern as sealed class types. The method of attempting the cast is a little different, since a failed cast can't return null, but the pattern should still be the same. If a struct implements a mutating interface (as is the case with e.g. List<T>.Enumerator, a struct that implements IEnumerator<T>), the proper behavior of equality comparisons is a bit murky.
Note, btw, that IComparable<T> and IEquatable<T> should be considered independent of each other. While it will often be the case that when X.CompareTo(Y) is zero, X.Equals(Y) will return true, some types may have a natural ordering where two things may be different without either ranking about the other. For example, one might have a NamedThing<T> type which combines a string and a T. Such a type would support a natural ordering on the name, but not necessarily on T. Two instances whose names match but whose T's differ should return 0 for CompareTo, but false for Equals. Because of this, overriding IComparable does not require overriding GetHashCode if Equals is not changed.
You certainly should override Equals(object t) - otherwise you may get incorrect results when that overload is used. You can't assume that the Equals(T other) is the overload that will be called.
If you don't override it, reference equality will be used, meaning that something like the following will return false:
myObject1.Id = 1;
myObject2.Id = 1;
myObject1.Equals((object)myObject2); // false!
Another possible problem is with inheriting classes - if you compare your type with an inheriting type, this can easily fail.
From msdn:
If you implement IEquatable, you should also override the base
class implementations of Object.Equals(Object) and GetHashCode so that
their behavior is consistent with that of the IEquatable.Equals
method. If you do override Object.Equals(Object), your overridden
implementation is also called in calls to the static
Equals(System.Object, System.Object) method on your class. In
addition, you should overload the op_Equality and op_Inequality
operators. This ensures that all tests for equality return consistent
results.
I could have done a better google search too. Here's a good article on msdn blogs by JaredPar on the subject.
In short, overriding Equals(object obj):
Needed for the static Equals(object obj1, object obj2) method on object class, or for Contains(object item) on ArrayList instance etc..
Accepting this since the link is more thorough on the subject. Thanks to Oded too..

should all c# classes implement Equals and GetHashCode?

should all c# classes override Equals and GetHashCode? For correctness
No, they already do.
Whether you have to override them, is up to how it will be used. In most cases, it is not needed.
All classes already inherit these methods from the base class, System.Object.
You can choose to override the methods in derived classes if you need to be able to compare two instances of an object beyond simple reference equality, otherwise it's not necessary.
Remember, however, that if you choose to override one of them, you also need to override the other in order to ensure that Hashtables and dictionary keys, among other things, work properly with you derived class. The GetHashCode method needs to reflect the same logic as the Equals method. See here for more explanations and examples:
http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx
and
http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx
All classes to inherit this from the System.Object.
If you need to provide a specific Equals or GetHashCode for a class then you should override the methods in your classes. Otherwise just leave them..
http://msdn.microsoft.com/en-us/library/system.object.gethashcode(v=VS.71).aspx
Maybe not all, but all classes that will be put into a some kind of bag (IList, ICollection, IDictionary, Hashset, etc.) and need some simple method to differentiate them (just think about Sort(), Contains(), BinarySearch(), etc.).
If you use a class that way you should definitely implement them correct.
When you override Equals, basically.
When you want to provide a different
idea of equality than simple reference
equality.
String is a good example of this - two
strings are equal (under a simple
Equals call) if they represent the
same sequence of characters. The hash
code reflects this, such that if two
strings are equal they will have the
same hash code. (The reverse isn't
necessarily true - two unequal strings
can have the same hash code, but it's
unlikely.)
(Strings are tricky in other ways,
mind you - there are lots of different
ideas of equality based on culture and
casing, but String.Equals just looks
at the UTF-16 code points which make
up the string, and compares them in
the simplest conceivable fashion.)
by Jon Skeet

Categories

Resources