Dictionary.ContainsKey() - How does it work? - c#

I've read the MSDN documentation on how Dictionary.ContainsKey() works, but I was wondering how it actually makes the equality comparison? Basically, I have a dictionary keyed to a reference type* and I want the ContainsKey() method to check a certain property of that reference type as its basis for determining if the key exists or not. For example, if I had a Dictionary(MyObject, int) and MyObject has a public property (of int) called "TypeID", could I get ContainsKey(MyObject myObject) to check to see if one of the keys has a TypeID that is equal to myObject? Could I just overload the == operator?
The reference type is an object called "Duration" which holds a value (double Length); "Duration" is a base type used in my music program to denote how long a particular sound lasts. I derive classes from it which incorporate more sophisticated timing concepts, like Western musical time signatures, but want all of them to be comparable in terms of their length.
EDIT: As suggested, I implemented IEquitable on my object like so:
public class Duration : IEquatable<Duration>
{
protected double _length;
/// <summary>
/// Gets or Sets the duration in Miliseconds.
/// </summary>
public virtual double Length
{
get
{
return _length;
}
set
{
_length = value;
}
}
// removed all the other code that as it was irrelevant
public override bool Equals(object obj)
{
Duration otherDuration = (Duration)obj;
if (otherDuration._length == _length)
{
return true;
}
else
{
return false
}
}
}
Is this all I need to do?

EDIT: here is code for your updated example. Note: I find it a little odd that you expose the field as protected, and also have a virtual property that exposes the member. Under this scheme something could override Length resulting in equality that looks at _lenght to not behave as expected.
public class Duration : IEquatable<Duration>
{
protected double _length;
/// <summary>
/// Gets or Sets the duration in Miliseconds.
/// </summary>
public virtual double Length
{
get { return _length; }
set { _length = value; }
}
// removed all the other code that as it was irrelevant
public bool Equals(Duration other)
{
// First two lines are just optimizations
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return _length.Equals(other._length);
}
public override bool Equals(object obj)
{
// Again just optimization
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
// Actually check the type, should not throw exception from Equals override
if (obj.GetType() != this.GetType()) return false;
// Call the implementation from IEquatable
return Equals((Duration) obj);
}
public override int GetHashCode()
{
// Constant because equals tests mutable member.
// This will give poor hash performance, but will prevent bugs.
return 0;
}
}
See EqualityComparer.Default for information on the default IEqualityComparer used by the Dictionary class.
If you do not want to generally override GetHashCode and Equals on the class, or if you are unable to. There is an overload of the Dictionary constructor in which you can provide the specific IEqualityComparer to use.
It is a simple interface to implement, but you do need to be careful that you respect the contract for GetHashCode or you can end up with unexpected behavior.
public class MyObjectEqualityComparer : IEqualityComparer<MyObject>
{
public bool Equals(MyObject x, MyObject y)
{
return x.TypeID == y.TypeID;
}
public int GetHashCode(MyObject obj)
{
return obj.TypeID; //Already an int
}
}
to use it just go
new Dictionary<MyObject, int>(new MyObjectEqualityComparer());
If you want to use the default IEqualityComparer you need to provide roughly the same methods on MyObjectEqualityComparer. You can avoid overriding object.Equals() if you implement IEquatable. However I would strongly discourage it because doing so can create some surprising behavior. You are better of overriding Equals so that you have consistent behavior for all calls to Equals and have hashing that properly matches Equals. I have had to fix a bug in inherited code caused by a past developer only implementing IEquatable.

Internally Dictionary uses EqualityComparer. Firstly it will check whether key implements IEquatable. If key doesn't implement this interface, it will call Equals method.

Related

Is it safe to override GetHashCode and get it from string property?

I have a class:
public class Item
{
public string Name { get; set; }
public override int GetHashCode()
{
return Name.GetHashCode();
}
}
The purpose of overriding GetHashCode is that I want to have only one occurence of an object with specified name in Dictionary.
But is it safe to get hash code from string?
In other words, is there any chance that two objects with different values of property Name would return the same hash code?
But is it safe to get hash code from string?
Yes, it is safe. But, what you're doing isn't. You're using a mutable string field to generate your hash code. Let's imagine that you inserted an Item as a key for a given value. Then, someone changes the Name string to something else. You now are no longer able to find the same Item inside your Dictionary, HashSet, or whichever structure you use.
More-so, you should be relying on immutable types only. I'd also advise you to implement IEquatable<T> as well:
public class Item : IEquatable<Item>
{
public Item(string name)
{
Name = name;
}
public string Name { get; }
public bool Equals(Item other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Item) obj);
}
public static bool operator ==(Item left, Item right)
{
return Equals(left, right);
}
public static bool operator !=(Item left, Item right)
{
return !Equals(left, right);
}
public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}
is there any chance that two objects with different values of property
Name would return the same hash code?
Yes, there is a statistical chance that such a thing will happen. Hash codes do not guarantee uniqueness. They strive for uni-formal distribution. Why? because your upper boundary is Int32, which is 32bits. Given the Pigenhole Principle, you may happen at end up with two different strings containing the same hash code.
Your class is buggy, because you have a GetHashCode override, but no Equals override. You also don't consider the case where Name is null.
The rule for GetHashCode is simple:
If a.Equals(b) then it must be the case that a.GetHashCode() == b.GetHashCode().
The more cases where if !a.Equals(b) then a.GetHashCode() != b.GetHashCode() the better, indeed the more cases where !a.Equals(b) then a.GetHashCode() % SomeValue != b.GetHashCode() % SomeValue the better, for any given SomeValue (you can't predict it) so we like to have a good mix of bits in the results. But the vital thing is that two objects considered equal must have equal GetHashCode() results.
Right now this isn't the case, because you've only overridden one of these. However the following is sensible:
public class Item
{
public string Name { get; set; }
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
public override bool Equals(object obj)
{
var asItem = obj as Item;
return asItem != null && Name == obj.Name;
}
}
The following is even better, because it allows for faster strongly-typed equality comparisons:
public class Item : IEquatable<Item>
{
public string Name { get; set; }
public override int GetHashCode()
{
return Name == null ? 0 : Name.GetHashCode();
}
public bool Equals(Item other)
{
return other != null && Name == other.Name;
}
public override bool Equals(object obj)
{
return Equals(obj as Item);
}
}
In other words, is there any chance that two objects with different values of property Name would return the same hash code?
Yes, this can happen, but it won't happen often, so that's fine. The hash-based collections like Dictionary and HashSet can handle a few collisions; indeed there'll be collisions even if the hash codes are all different because they're modulo'd down to a smaller index. It's only if this happens a lot that it impacts performance.
Another danger is that you'll be using a mutable value as a key. There's a myth that you shouldn't use mutable values for hash-codes, which isn't true; if a mutable object has a mutable property that affects what it is considered equal with then it must result in a change to the hash-code.
The real danger is mutating an object that is a key to a hash collection at all. If you are defining equality based on Name and you have such an object as the key to a dictionary then you must not change Name while it is used as such a key. The easiest way to ensure that is to have Name be immutable, so that is definitely a good idea if possible. If it is not possible though, you need to be careful just when you allow Name to be changed.
From a comment:
So, even if there is a collision in hash codes, when Equals will return false (because the names are different), the Dictionary will handle propertly?
Yes, it will handle it, though it's not ideal. We can test this with a class like this:
public class SuckyHashCode : IEquatable<SuckyHashCode>
{
public int Value { get; set; }
public bool Equals(SuckyHashCode other)
{
return other != null && other.Value == Value;
}
public override bool Equals(object obj)
{
return Equals(obj as SuckyHashCode);
}
public override int GetHashCode()
{
return 0;
}
}
Now if we use this, it works:
var dict = Enumerable.Range(0, 1000).Select(i => new SuckyHashCode{Value = i}).ToDictionary(shc => shc);
Console.WriteLine(dict.ContainsKey(new SuckyHashCode{Value = 3})); // True
Console.WriteLine(dict.ContainsKey(new SuckyHashCode{Value = -1})); // False
However, as the name suggests, it isn't ideal. Dictionaries and other hash-based collections all have means to deal with collisions, but those means mean that we no longer have the great nearly O(1) look-up, but rather as the percentage of collisions gets greater the look-up approaches O(n). In the case above where the GetHashCode is as bad as it could be without actually throwing an exception, the look-up would be O(n) which is the same as just putting all the items into an unordered collection and then finding them by looking at every one to see if it matches (indeed, due to differences in overheads, it's actually worse than that).
So for this reason we always want to avoid collisions as much as possible. Indeed, to not just avoid collisions, but to avoid collisions after the result has been modulo'd down to make a smaller hash code (because that's what happens internally to the dictionary).
In your case though because string.GetHashCode() is reasonably good at avoiding collisions, and because that one string is the only thing that equality is defined by, your code would in turn be reasonably good at avoiding collisions. More collision-resistant code is certainly possible, but comes at a cost to performance in the the code itself* and/or is more work than can be justified.
*(Though see https://www.nuget.org/packages/SpookilySharp/ for code of mine that is faster than string.GetHashCode() on large strings on 64-bit .NET and more collision-resistant, though it is slower to produce those hash codes on 32-bit .NET or when the string is short).
Instead of using GetHashCode to prevent duplicates to be added to a dictionary, which is risky in your case as explained already, I would recommend to use a (custom) equality comparer for your dictionary.
If the key is an object, you should create an own equality comparer that compares the string Name value. If the key is the string itself, you can use StringComparer.CurrentCulture for example.
Also in this case it is key to make the string immutable, since else you might invalidate your dictionary by changing the Name.

Equals/GetHashCode override warning in derived class with no state

I've created a strongly-typed, immutable wrapper class for various string IDs that flow through our system
The abstract BaseId class:
(some error-checking and formatting omitted for brevity...)
public abstract class BaseId
{
// Gets the type name of the derived (concrete) class
protected abstract string TypeName { get; }
protected internal string Id { get; private set; }
protected BaseId(string id) { Id = id; }
// Called by T.Equals(T) where T is a derived type
protected bool Equals(BaseId other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
return String.Equals(Id, other.Id);
}
// warning CS0660 (see comment #1 below)
//public override bool Equals(object obj) { return base.Equals(obj); }
public override int GetHashCode()
{
return TypeName.GetHashCode() * 17 + Id.GetHashCode();
}
public override string ToString()
{
return TypeName + ":" + Id;
}
// All T1 == T2 comparisons come here (where T1 and T2 are one
// or more derived types)
public static bool operator ==(BaseId left, BaseId right)
{
// Eventually calls left.Equals(object right), which is
// overridden in the derived class
return Equals(left, right);
}
public static bool operator !=(BaseId left, BaseId right)
{
// Eventually calls left.Equals(object right), which is
// overridden in the derived class
return !Equals(left, right);
}
}
My goal was keep as much of the implementation in the base class so that the derived classes would be small, consisting mostly/entirely of boilerplate code.
Example concrete DerivedId class:
Note that this derived type defines no additional state of its own. Its purpose is solely to create a strong type.
public sealed class DerivedId : BaseId, IEquatable<DerivedId>
{
protected override string TypeName { get { return "DerivedId"; } }
public DerivedId(string id) : base(id) {}
public bool Equals(DerivedId other)
{
// Method signature ensures same (or derived) types, so
// defer to BaseId.Equals(object) override
return base.Equals(other);
}
// Override this so that unrelated derived types (e.g. BarId)
// NEVER match, regardless of underlying Id string value
public override bool Equals(object obj)
{
// Pass obj or null for non-DerivedId types to our
// Equals(DerivedId) override
return Equals(obj as DerivedId);
}
// warning CS0659 (see comment #2 below)
//public override int GetHashCode() { return base.GetHashCode(); }
}
Each class is generating a compiler warning:
Not overriding Object.Equals(object o) in BaseId generates a compile warning:
warning CS0660: 'BaseId' defines operator == or operator != but does not override Object.Equals(object o)
But if I implement BaseId.Equals(object o), it'll just be to call the base class implementation in Object.Equals(object o). I don't see how this will ever get called anyway; it's always overridden in the derived class, and the implementation there doesn't call up to this implementation.
Not overriding BaseId.GetHashCode() in DerivedId generates a compile warning:
warning CS0659: 'DerivedId' overrides Object.Equals(object o) but does not override Object.GetHashCode()
This derived class has no additional state, so there's nothing for me to do in an implementation of DerivedId.GetHashCode(), except to call the base class implementation in BaseId.GetHashCode().
I can suppress the compiler warnings or just implement the methods and have them call the base class implementations, but I want to make sure I'm not missing something.
Is there something odd about the way I did this, or is this just one of those things that you have to do to suppress warnings on otherwise correct code?
The reason those are warnings rather than errors is that the code will still work (probably), but it might do things that you don't expect. The warning is a big red flag that says, "Hey! You might be doing something bad here. You might want to take another look at it."
As it turns out, the warning is right on.
In this particular case, it's possible that some code can call Object.Equals(object) on one of your BaseId objects. For example, somebody could write:
bool CompareThings(BaseId thing, object other)
{
return thing.Equals(other);
}
The compiler will generate a call to Object.Equals(object) because your BaseId type doesn't override it. That method will do the default comparison, which is the same as Object.ReferenceEquals(object). So you have two different meanings of Equals. You need to override Object.Equals(object) and have it call Equals(BaseId) after checking that the object being compared is indeed of type BaseId.
In the second case, you're right: there probably isn't a need to override GetHashCode, since the object doesn't define any new fields or do anything that changes the meaning of Equals. But the compiler doesn't know that. Sure, it knows that you didn't add any fields, but you did override Equals, meaning that you potentially changed the meaning of equality. And if you changed the meaning of equality, then you very likely changed (or should change) how hash codes are computed.
Not handling equality properly is a very common cause of mistakes when designing new types. It's a good thing that the compiler is overly cautious in this area.
It is generally not good for classes to have more than one overridable (virtual or abstract) Equals method. Either have derived classes override Equals(object) themselves, or else have a sealed base implementation of Equals(object) (and possibly GetHashCode()) chain to an abstract or virtual Equals(BaseId) (and possibly GetDerivedHashCode()). It's unclear what exactly your goal is, though I would suggest that if things always supposed to be equal if ID and type both match, and unequal if ID or type doesn't match, your base types shouldn't need to include any equality checking; simply have the base equality check test whether the types match (probably using GetType() rather than TypeName).
I should mention, btw, that I generally dislike classes which overload == and != unless they are supposed to fundamentally behave as values. In C#, the == operator can either call an overloaded equality-check operator or test reference equality; compare the effects of:
static bool IsEqual1<T>(T thing1, thing2) where T:class
{
return thing1 == thing2;
}
static bool IsEqual2<T>(T thing1, thing2) where T:BaseId
{
return thing1 == thing2;
}
The first method above will perform a reference equality test even if T overloads the equality-check operator. In the second, it will use BaseId's overload. Visually, it's not exactly clear that the BaseId constraint should have such an effect, but it does. In vb.net, there would be no confusion since vb.net would not allow the overloadable equality-test operator in the IsEqual1; if a reference-equality test was desired in that method (or in the second, for that matter), code would have to use the Is operator. Since C# uses the same token as both a reference-equality test and an overloadable equality test, however, the binding of the == token isn't always obvious.
Addressing issue #2 in the question:
Not overriding BaseId.GetHashCode() in DerivedId generates a compile warning:
Run the following code with the GetHashCode() method commented out, then again without commenting it out, you'll see that when there is no implementation of GetHashCode the set contains two instances of Person, but when you add the implementation of GetHashCode the set contains only one instance demonstrating that some operations/classes use GetHashCode for comparison.
class Program
{
static void Main(string[] args)
{
Person p1 = new Person() { FirstName="Joe", LastName = "Smith"};
Person p2 = new Person() { FirstName="Joe", LastName ="Smith"};
ISet<Person> set = new HashSet<Person>();
set.Add(p1);
set.Add(p2);
foreach (var item in set)
{
Console.WriteLine(item.FirstName);
}
}
}
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override bool Equals(object obj)
{
if (obj == null) return false;
var that = obj as Person;
if (that == null) return false;
return
FirstName == that.FirstName &&
LastName == that.LastName;
}
public override int GetHashCode() //run the code with and without this method
{
int hashCode = 1938039292;
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(FirstName);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(LastName);
return hashCode;
}
}

Implemeting GetHashCode and Equals methods for ValueObjects

There is a passage from NHibernate documentation:
Note: if you define an ISet of composite elements, it is very important to implement Equals() and GetHashCode() correctly.
What does correctly mean there? Is it neccessary to implement those methods for all value objects in domain?
EXTENDING MY QUESTION
In the article Marc attached user Albic states:
It's actually very hard to implement GetHashCode() correctly because, in addition to the rules Marc already mentioned, the hash code should not change during the lifetime of an object. Therefore the fields which are used to calculate the hash code must be immutable.
I finally found a solution to this problem when I was working with NHibernate. My approach is to calculate the hash code from the ID of the object. The ID can only be set though the constructor so if you want to change the ID, which is very unlikely, you have to create a new object which has a new ID and therefore a new hash code. This approach works best with GUIDs because you can provide a parameterless constructor which randomly generates an ID.
I suddenly realized what I've got inside my AbstractEntity class:
public abstract class AbstractEntity<T> where T : AbstractEntity<T> {
private Nullable<Int32> hashCode;
public virtual Guid Id { get; protected set; }
public virtual Byte[] Version { get; set; }
public override Boolean Equals(Object obj) {
var other = obj as T;
if(other == null) {
return false;
}
var thisIsNew = Equals(this.Id, Guid.Empty);
var otherIsNew = Equals(other.Id, Guid.Empty);
if(thisIsNew && otherIsNew) {
return ReferenceEquals(this, other);
}
return this.Id.Equals(other.Id);
} // public override Boolean Equals(Object obj) {
public override Int32 GetHashCode() {
if(this.hashCode.HasValue) {
return this.hashCode.Value;
}
var thisIsNew = Equals(this.Id, Guid.Empty);
if(thisIsNew) {
this.hashCode = base.GetHashCode();
return this.hashCode.Value;
}
return this.Id.GetHashCode();
} // public override Int32 GetHashCode() {
public static Boolean operator ==(AbstractEntity<T> l, AbstractEntity<T> r) {
return Equals(l, r);
}
public static Boolean operator !=(AbstractEntity<T> l, AbstractEntity<T> r) {
return !Equals(l, r);
}
} // public abstract class AbstractEntity<T>...
As all components are nested within entities should I then implement Equals() and GetHashCode() for them?
Correctly means that GetHashCode returns the same hash code for the entities that are expected to be equal. Because equality of 2 entities is made by comparison of that code.
On the other side, that means that for entities that are not equal, the uniqueness of hash code has to be guaranteed, as much as it possible.
The documentation for Equals and GetHashCode explain this well and include specific guidance on implementation for value objects. For value objects, Equals is true if the objects are the same type and the public and private fields are equal. However, this explanation applies to framework value types and you are free to create your own Equals by overriding it.
GetHashCode has two rules that must be followed:
If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not
compare as equal, the GetHashCode methods for the two object do not
have to return different values.
The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state
that determines the return value of the object's Equals method. Note
that this is true only for the current execution of an application,
and that a different hash code can be returned if the application is
run again.

Comparing objects

I have a class it contains some string members, some double members and some array objects.
I create two objects of this class, is there any simplest, efficient way of comparing these objects and say their equal? Any suggestions?
I know how to write a compare function, but will it be time consuming.
The only way you can really do this is to override bool Object.Equals(object other) to return true when your conditions for equality are met, and return false otherwise. You must also override int Object.GetHashCode() to return an int computed from all of the data that you consider when overriding Equals().
As an aside, note that the contract for GetHashCode() specifies that the return value must be equal for two objects when Equals() would return true when comparing them. This means that return 0; is a valid implementation of GetHashCode() but it will cause inefficiencies when objects of your class are used as dictionary keys, or stored in a HashSet<T>.
The way I implement equality is like this:
public class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
if (other == null)
return false;
if (other == this)
return true; // Same object reference.
// Compare this to other and return true/false as appropriate.
}
public override bool Equals(Object other)
{
return Equals(other as Foo);
}
public override int GetHashCode()
{
// Compute and return hash code.
}
}
A simple way of implementing GetHashCode() is to XOR together the hash codes of all of the data you consider for equality in Equals(). So if, for example, the properties you compare for equality are string FirstName; string LastName; int Id;, your implementation might look like:
public override int GetHashCode()
{
return (FirstName != null ? FirstName.GetHashCode() : 0) ^
(LastName != null ? LastName.GetHashCode() : 0) ^
Id; // Primitives of <= 4 bytes are their own hash codes
}
I typically do not override the equality operators, as most of the time I'm concerned with equality only for the purposes of dictionary keys or collections. I would only consider overriding the equality operators if you are likely to do more comparisons by value than by reference, as it is syntactically less verbose. However, you have to remember to change all places where you use == or != on your object (including in your implementation of Equals()!) to use Object.ReferenceEquals(), or to cast both operands to object. This nasty gotcha (which can cause infinite recursion in your equality test if you are not careful) is one of the primary reasons I rarely override these operators.
The 'proper' way to do it in .NET is to implement the IEquatable interface for your class:
public class SomeClass : IEquatable<SomeClass>
{
public string Name { get; set; }
public double Value { get; set; }
public int[] NumberList { get; set; }
public bool Equals(SomeClass other)
{
// whatever your custom equality logic is
return other.Name == Name &&
other.Value == Value &&
other.NumberList == NumberList;
}
}
However, if you really want to do it right, this isn't all you should do. You should also override the Equals(object, object) and GetHashCode(object) methods so that, no matter how your calling code is comparing equality (perhaps in a Dictionary or perhaps in some loosely-typed collection), your code and not reference-type equality will be the determining factor:
public class SomeClass : IEquatable<SomeClass>
{
public string Name { get; set; }
public double Value { get; set; }
public int[] NumberList { get; set; }
/// <summary>
/// Explicitly implemented IEquatable method.
/// </summary>
public bool IEquatable<SomeClass>.Equals(SomeClass other)
{
return other.Name == Name &&
other.Value == Value &&
other.NumberList == NumberList;
}
public override bool Equals(object obj)
{
var other = obj as SomeClass;
if (other == null)
return false;
return ((IEquatable<SomeClass>)(this)).Equals(other);
}
public override int GetHashCode()
{
// Determine some consistent way of generating a hash code, such as...
return Name.GetHashCode() ^ Value.GetHashCode() ^ NumberList.GetHashCode();
}
}
Just spent the whole day writing an extension method looping through reflecting over properties of an object with various complex bits of logic to deal with different property type and actually got it close to good, then at 16:55 it dawned on me that if you serialize the two object, you simply need compare the two strings ... duh
So here is a simple serializer extension method that even works on Dictionaries
public static class TExtensions
{
public static string Serialize<T>(this T thisT)
{
var serializer = new DataContractSerializer(thisT.GetType());
using (var writer = new StringWriter())
using (var stm = new XmlTextWriter(writer))
{
serializer.WriteObject(stm, thisT);
return writer.ToString();
}
}
}
Now your test can be as simple as
Asset.AreEqual(objA.Serialise(), objB.Serialise())
Haven't done extensive testing yet, but looks promising and more importantly, simple. Either way still a useful method to have in your utility set right ?
The best answer is to implement IEquatable for your class - it may not be the answer you want to hear, but that's the best way to implement value equivalence in .NET.
Another option would be computing a unique hash of all of the members of your class and then doing value comparisons against those, but that's even more work than writing a comparison function ;)
Since these are objects my guess is that you will have to override the Equals method for objects. Otherwise the Equals method will give you ok only if both objects refering to the same object.
I know this is not the answer you want. But since there is little number of properties in your class you can easily override the method.

IComparable and Equals()

From MSDN
Types that implement IComparable must override Equals.Types that override Equals must also override GetHashCode; otherwise, Hashtable might not work correctly.
I didn't quite get it. Can anyone explain.
IComparable is an interface that defines that two instances of the implementing class can be seen as greater than, less than or equal to one another. Since you have defined equality in the methods of that interface you also need to override the Equals method (and equality operator) to ensure the results from the two are consistent.
public class EqualityTest : IComparable<EqualityTest>
{
public int Value { get; set; }
public int CompareTo(EqualityTest other)
{
return this.Value.CompareTo(other.Value);
}
}
In the above example I have implemented IComparable, but not overridden Equals. If you call CompareTo with two separate instances of the class that have the same Value it will say there are equal. If you call Equals with the same two instances it will say they are not equal as it will test to see if they are the same object (the default implementation of Equals).
Two equal items should return the same hash code (which are used for quickly finding items used as keys in hash tables) so if you override Equals then you should also override GetHashCode()
As an example I just created the following class in my IDE:
public class EqualityTest
{
public string A { get; set; }
public string B { get; set; }
}
And ran Resharper's helpful "Generate Equality" function saying that I wanted both A and B to affect equality. This is the code it created:
public bool Equals(EqualityTest other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(other.A, A) && Equals(other.B, B);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof(EqualityTest))
{
return false;
}
return Equals((EqualityTest)obj);
}
public override int GetHashCode()
{
unchecked
{
return ((A != null ? A.GetHashCode() : 0)*397) ^ (B != null ? B.GetHashCode() : 0);
}
}
public static bool operator ==(EqualityTest left, EqualityTest right)
{
return Equals(left, right);
}
public static bool operator !=(EqualityTest left, EqualityTest right)
{
return !Equals(left, right);
}
So if you are overriding Equals then you should also define all of the above to ensure consistency, if you are implementing IComparable then the same applies.
IComparable is used for comparing two objects - if these are considered equal then Compare will return 0. It would be very unexpected if IComparable.Compare returned zero for two objects, yet obj1.Equals(obj2) returned false since this would imply two different meanings of equality for the objects.
When a class overrides Equals, it should also override GetHashCode since two equal objects should hash to the same value, and this hash should be based on the fields/properties used in the implementation of equality.
There are two ways that objects in your code could be compared: Equals and GetHashCode
For your object to be compared properly in ALL situations, when you override the Equals method (used for some comparisons), you must also override GetHashCode (used in the rest).
If you override one but not the other, depending on your code, you could get unexpected results.

Categories