IEquatable on POCO identity field - c#

I have POCOs from a SQL Server database that have an identity ID field. I would like to implement IEquatable so I can check if they're the same record, use .Contains() on List<T> etc.
Assuming I will never need to compare unsaved instances, is it sufficient to just implement using:
public bool Equals(MyClass other)
{
return this.ID == other.ID;
}
public override int GetHashCode()
{
return this.ID;
}
I'm about to do this, but just wanted to check that's fine, as I'm not 100% sure what GetHashCode has to do with it, or if there are any other considerations (such as the "unsaved instance" one, which I can discard) that I'm not aware of.

You need to override GetHashCode() whenever you override Equals() (which you should also do when you implement IEquatable<T>) so that classes that rely on it, such as Dictionary<TKey, TValue>, can handle it correctly. It's one of those inner details that annoyingly leaks out. More information.
As for whether comparing your IDs is sufficient for determining equality, if you're absolutely sure that two entities with the same ID should be considered equivalent under all circumstances, then go for it. That's a question only the requirements of your application can answer. I've done it quite a few times myself, almost always with read-only entities.
I would implement it this way:
// IEquatable<MyClass> implementation
public bool Equals(MyClass other)
{
if (other == null)
{
return false;
}
return this.ID == other.ID;
}
// Override of default Object.Equals()
public override bool Equals(object other)
{
return this.Equals(other as MyClass);
}
public override int GetHashCode()
{
// Call ID.GetHashCode() because ID may not always be int,
// and the ID type might have a more useful implementation
// of GetHashCode() to offer.
return this.ID.GetHashCode();
}
You should probably also override the == and != operators in this case. You can always use Object.ReferenceEquals() if you want to distinguish between two distinct references with the same ID.

If, by your businessrules, your objects are equal to each other when their ID's are equal, than you are doing just fine!
The hashcode of an object state that every object with a different state, should return a different hashcode. This hashcode is not unique, but we can try to make them as different as possible. Just check out the hashcode of strings "Hello".GetHashCode() and "Goodbye".GetHashCode().
HashCode is used in some collections, like System.Collections.Generic.Dictionary<K,V> and System.Collections.Generic.HashSet<T>. By using this hashcode, those dictionary can search and find an item very fast (almost an O(1)-operation) by using this 'unique' hashcode. Calculate this hashcode by using the hashcodes of the field(s) of your entity.
If you implement IEquatable<T>.Equals, also override Equals(object).

Related

Why does Equals(object) win over Equals(T) when using an inherited object in Hashset or other Collections?

I am aware of the fact that I always have to override Equals(object) and GetHashCode() when implementing IEquatable<T>.Equals(T).
However, I don't understand, why in some situations the Equals(object) wins over the generic Equals(T).
For example why is the following happening? If I declare IEquatable<T> for an interface and implement a concrete type X for it, the general Equals(object) is called by a Hashset<X> when comparing items of those type against each other. In all other situations where at least one of the sides is cast to the Interface, the correct Equals(T) is called.
Here's a code sample to demonstrate:
public interface IPerson : IEquatable<IPerson> { }
//Simple example implementation of Equals (returns always true)
class Person : IPerson
{
public bool Equals(IPerson other)
{
return true;
}
public override bool Equals(object obj)
{
return true;
}
public override int GetHashCode()
{
return 0;
}
}
private static void doEqualityCompares()
{
var t1 = new Person();
var hst = new HashSet<Person>();
var hsi = new HashSet<IPerson>();
hst.Add(t1);
hsi.Add(t1);
//Direct comparison
t1.Equals(t1); //IEquatable<T>.Equals(T)
hst.Contains(t1); //Equals(object) --> why? both sides inherit of IPerson...
hst.Contains((IPerson)t1); //IEquatable<T>.Equals(T)
hsi.Contains(t1); //IEquatable<T>.Equals(T)
hsi.Contains((IPerson)t1); //IEquatable<T>.Equals(T)
}
HashSet<T> calls EqualityComparer<T>.Default to get the default equality comparer when no comparer is provided.
EqualityComparer<T>.Default determines if T implementsIEquatable<T>. If it does, it uses that, if not, it uses object.Equals and object.GetHashCode.
Your Person object implements IEquatable<IPerson> not IEquatable<Person>.
When you have a HashSet<Person> it ends up checking if Person is an IEquatable<Person>, which its not, so it uses the object methods.
When you have a HashSet<IPerson> it checks if IPerson is an IEquatable<IPerson>, which it is, so it uses those methods.
As for the remaining case, why does the line:
hst.Contains((IPerson)t1);
call the IEquatable Equals method even though its called on the HashSet<Person>. Here you're calling Contains on a HashSet<Person> and passing in an IPerson. HashSet<Person>.Contains requires the parameter to be a Person; an IPerson is not a valid argument. However, a HashSet<Person> is also an IEnumerable<Person>, and since IEnumerable<T> is covariant, that means it can be treated as an IEnumerable<IPerson>, which has a Contains extension method (through LINQ) which accepts an IPerson as a parameter.
IEnumerable.Contains also uses EqualityComparer<T>.Default to get its equality comparer when none is provided. In the case of this method call we're actually calling Contains on an IEnumerable<IPerson>, which means EqualityComparer<IPerson>.Default is checking to see if IPerson is an IEquatable<IPerson>, which it is, so that Equals method is called.
Although IComparable<in T> is contravariant with respect to T, such that any type which implements IComparable<Person> would automatically be considered an implementation of IComparable<IPerson>, the type IEquatable<T> is intended for use with sealed types, especially structures. The requirement that Object.GetHashCode() be consistent with both IEquatable<T>.Equals(T) and Object.Equals(Object) generally implies that the latter two methods should behave identically, which in turn implies that one of them should chain to the other. While there is a large performance difference between passing a struct directly to an IEquatable<T> implementation of the proper type, compared with constructing a instance of the structure's boxed-heap-object type and having an Equals(Object) implementation copy the structure data out of that, no such performance different exists with reference types. If IEquatable<T>.Equals(T) and Equals(Object) are going to be equivalent and T is an inheritable reference type, there's no meaningful difference between:
bool Equals(MyType obj)
{
MyType other = obj as MyType;
if (other==null || other.GetType() != typeof(this))
return false;
... test whether other matches this
}
bool Equals(MyType other)
{
if (other==null || other.GetType() != typeof(this))
return false;
... test whether other matches this
}
The latter could save one typecast, but that's unlikely to make a sufficient performance difference to justify having two methods.

Why doesn't Contains call CompareTo nor Equals?

While comparing instances of a custom class, I noticed that a call to Contains doesn't work the way I expect it to. Assuming that the default comparison goes by the reference (pointer or whatever it's called), I implemented both CompareTo and Equals. I made sure to be implementing IComparable, of course.
It's still doesn't work and I get no hits when I put breakpoints on those methods.
What can I be missing and is the best option to use extension methods if I'm not?
public override bool Equals(Object input)
{
return Id == ((MyType) input).Id;
}
public int CompareTo(Object input)
{
return Id - ((MyType)input).Id;
}
A better implementation could be:
public bool Equals(MyType other)
{
// if 'other' is a null reference, or if 'other' is more derived or less derived
if ((object)other == (object)null || other.GetType() != GetType())
return false;
// OK, check members (assuming 'Id' has a type that makes '==' a wise choice)
return Id == other.Id;
}
public override bool Equals(object obj)
{
// call to other overload
return Equals(obj as MyType);
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
You can mark the class as implementing IEquatable<MyType> in that case (but it will work even without that).
Regarding GetHashCode: Always remember to override it. You should have seen a compiler warning that it was problematic to override Equals(object) without overriding GetHashCode. Never keep the code return base.GetHashCode() in the override (assuming the base class is System.Object). Either give it a try and implement something based on the members that participate in Equals. If you do not think GetHashCode will actually be used in your case, say:
public override int GetHashCode()
{
throw new NotSupportedException("We don't have GetHashCode, sorry");
}
If you absolutely know that you will only be using List<>.Contains, and not e.g. Dictionary<,>, HashSet<> and not Linq's Distinct(), etc. etc., it could work with GetHashCode() simply throwing.
IComparable<MyType> is not needed unless you sort List<MyType> or MyType[], or you use Linq's OrderBy with MyType, or you use SortedDictionary<,>, SortedSet<>.
Overloading operator == is not needed for these uses.

Are custom objects equal by value with List<>

I have the following classes:
public class MyDocuments
{
public DateTime registeredDate;
public string version;
public List<Document> registeredDocuments;
}
public class Document
{
public string name;
public List<File> registeredFiles;
}
public class File
{
public string name;
public string content;
}
I have an instance of MyDocuments which has several documents in List<Document> registeredDocument. I get a new List<Document> from the user.
How can I verify that the new object doesn't exist in the list? I want to compare by value not reference.
I'm thinking of using HashSet instead of List. Is this the proper approach?
How are equality comparisons performed?
Whenever the BCL classes want to perform an equality check between objects of some type T, they do so by calling one or both of the methods in some implementation of IEqualityComparer<T>. To get hold of such an implementation, the framework looks to EqualityComparer<T>.Default.
As mentioned in the documentation, this property produces an IEqualityComparer<T> like this:
The Default property checks whether type T implements the
System.IEquatable<T> interface and, if so, returns an
EqualityComparer<T> that uses that implementation. Otherwise, it
returns an EqualityComparer<T> that uses the overrides of
Object.Equals and Object.GetHashCode provided by T.
What are my options?
So, in general, to dictate how equality comparisons should be performed you can:
Explicitly provide an implementation of IEqualityComparer<T> to the class or method that performs equality checks. This option is not very visible with List<T>, but many LINQ methods (such as Contains) do support it.
Make your class implement IEquatable<T>. This will make EqualityComparer<T>.Default use this implementation, and is a good choice whenever there is an obvious "natural" way to compare objects of type T.
Override object.GetHashCode and object.Equals without implementing IEqualityComparer<T>. However, this is simply an inferior version of #2 and AFAIK should always be avoided.
Which option to pick?
A good rule of thumb is: if there is an obvious and natural way to compare objects of class T, consider having it implement IEquatable<T>; this will make sure your comparison logic is used throughout the framework without any additional involvement. If there is no obvious candidate, or if you want to compare in a manner different than the default, implement your own IEqualityComparer<T> and pass the implementation as an argument to the class or method that needs to perform equality checks.
You will need to implement the Equals() method, and probably GetHashCode() as well. See this answer for an example.
You should implement IEquatable<T>.
When you implement this interface on your custom object, any equality checks (e.g. Contains, IndexOf) are automatically done using your objects implementation.
override the object.Equals method.
here's an example straight from the documentation
public class Person
{
private string idNumber;
private string personName;
public Person(string name, string id)
{
this.personName = name;
this.idNumber = id;
}
public override bool Equals(Object obj)
{
Person personObj = obj as Person;
if (personObj == null)
return false;
else
return idNumber.Equals(personObj.idNumber);
}
public override int GetHashCode()
{
return this.idNumber.GetHashCode();
}
}
the Equals method returns a bool which is whether or not obj is equal to this
Something like this at the top level, continued down at the sub-levels:
public class MyDocuments
{
public DateTime registeredDate;
public string version;
public HashSet<Document> registeredDocuments;
public override bool Equals(Object o)
{
if( !(o is MyDocuments) ) return false;
MyDocuments that = (MyDocuments)o;
if( !String.Equals(this.version, that.version) ) return false;
if( this.registeredDocuments.Count != that.registeredDocuments.Count ) return false;
// assuming registeredDate doesn't matter for equality...
foreach( Document d in this.registeredDocuments )
if( !that.registeredDocuments.Contains(d) )
return false;
return true;
}
public override int GetHashCode()
{
int ret = version.GetHashCode();
foreach (Document d in this.registeredDocuments)
ret ^= d.GetHashCode(); // xor isn't great, but better than nothing.
return ret;
}
}
Note: Caching could be useful for the HashCode values if the properties were change-aware.

Is there a built-in IEqualityComparer that compares objects only using their hash value?

Is there a built-in IEqualityComparer that compares objects by the value returned by their GetHashCode value? It's easy to write, but I'd prefer to use a provided class instead of a custom one.
Current code:
private class HashComparer : IEqualityComparer<TKey>
{
private readonly Func<TKey, int> _Hasher;
public HashComparer (Func<TKey, int> hasher)
{
_Hasher = hasher;
}
public bool Equals (TKey x, TKey y)
{
// null supposed to throw, therefore no check
return _Hasher (x) == _Hasher (y);
}
public int GetHashCode (TKey obj)
{
return _Hasher (obj);
}
}
No, such a thing doesn't exist in the framework as far as I'm aware.
It would be a generally Bad Thing - hash codes don't have to be unique, so it couldn't be used to mimic normal equality, other than for types with 2^32 possible values or fewer, and a hash generation algorithm which gives a unique code for each value.
I'm struggling to think of any sensible use for this - which is why you're unlikely to find it in the framework. Perhaps there's some very specialized situation where you'd find it useful, but that's not enough justification to put it in the main framework.
Out of interest, what are you trying to do with it?

C# - List<T>.Remove() always deletes the first object on the list

Working in Visual Studio 2008 (C#)...
I use a List collection to store instances of my custom class (Shift).
I want to delete a certain shift from the list by using the Remove method.
But List.Remove() always deletes the first item it finds.
I've implemented the IComparable interface for my Shift, I thought this would be enough, then I added an implementation of IEqualityComparer, and it still has no effect.
Here's the excerpt with my implementation:
region IComparable Members
public int CompareTo(object obj)
{
Shift s1 = this;
Shift s2 = (Shift)obj;
if (s1.start.time != s2.start.time)
return s1.start.CompareTo(s2.start);
else
return s1.end.CompareTo(s2.end);
}
endregion
region IEqualityComparer Members
public bool Equals(Shift x, Shift y)
{
if ((x.opening) != (y.opening)) return false;
if ((x.closing) != (y.closing)) return false;
if (!x.opening) if (x._start != y._start) return false;
if (!x.closing) if (x._end != y._end) return false;
if (x.when != y.when) return false;
if (x.day != y.day) return false;
if (x.EmployeeID != y.EmployeeID) return false;
return true;
}
public int GetHashCode(Shift obj)
{
return obj.ToString().ToLower().GetHashCode();
}
endregion
And yet, still - when the List contains two shifts, say "8:00 - 15:00"; "12:00 - 16:00", calling Remove("12:00-16:00") results in "8:00 - 15:00" getting removed, and the latter one remains in the collection!
What's wrong here? Thx
You can override object.GetHashCode and object.Equals:
public override bool Equals(object obj)
{
if(obj == null)
{
return false;
}
return Equals(this, obj as Shift);
}
public override int GetHashCode()
{
return this.GetHashCode(this);
}
You should also probably do a null check in Equals(x, y).
IComparable is not normally used to compare for equality (it's used for ordering), so List<T>.Remove() ignores it.
IEqualityComparer is not an equivalent of IComparable for equality purposes. It is supposed to be implemented by a comparer object - that is, an object that compares other objects for equality. If you want equality comparisons to be inherent to your class, then you rather need to implement IEquatable<T>. Or just override Object.Equals() and Object.GetHashCode() on your class, without implementing any interfaces.
Remove uses EqualityComparer<T>.Default to determine equality and choose which object to remove, which will use IEquatable<T> if it's implemented on your object, otherwise, it will use reference equality.
You have two options to get the behavior you want:
1) Make Shift implement IEquatable<T> (not just override Object.Equals or make the method, but make Shift - Shift : IEquatable<Shift>)
2) Use List<T>.RemoveAt
With the example you provided, you're calling:
List<Shift>.Remove("12:00 - 16:00");
"12:00 - 16:00" in this case, is a String value and not an actual Shift object. Make sure that in your CompareTo method that you're code is properly casting the String value to a Shift object. Otherwise, when it compares start times...things could go haywire.

Categories

Resources