Contains() method of List<T> - c#

Precisely which methods in a Class are responsible for the List<T>'s Contains() to operate?
I have overloaded == in my class. But it seems to have no effect.

It will just call Equals() - that's all that needs to be overridden (or not, if you're happy with reference identity comparisons). If the type implements IEquatable<T> then that implementation will be used in preference to the general Equals(object) though.
In particular, from the documentation for List<T>.Contains:
This method determines equality using
the default equality comparer
EqualityComparer(T).Default for T, the
type of values in the list.
And from EqualityComparer<T>.Default:
The Default property checks whether
type T implements the
System.IEquatable(T) generic 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.
I don't believe it will use GetHashCode at all though.

From the .NET guidelines - if you implement == always provide an implementation for object.Equals() and the != operator. The reason is that operators are not part of any interface and their use is not allowed in generic implementations (a List class cannot call the == operator on T because there is no guarantee that T will have the operator defined (see structs for example)).

It will either call Object.Equals() or, if you have implemented, IEquatable<T>.Equals():
private static EqualityComparer<T> CreateComparer()
{
Type c = typeof(T);
if (c == typeof(byte))
{
return (EqualityComparer<T>) new ByteEqualityComparer();
}
if (typeof(IEquatable<T>).IsAssignableFrom(c))
{
return (EqualityComparer<T>) typeof(GenericEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(c);
}
if (c.IsGenericType && (c.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
Type type2 = c.GetGenericArguments()[0];
if (typeof(IEquatable<>).MakeGenericType(new Type[] { type2 }).IsAssignableFrom(type2))
{
return (EqualityComparer<T>) typeof(NullableEqualityComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(type2);
}
}
return new ObjectEqualityComparer<T>();
}

Related

do I need to override Equal?

I've read some stuff about overriding Equal and GetHashcode but do I need it when just having a simple own Equal method like below?
And if I do have to override it:
Why is that? - Should I use Id.GetHashCode() when overriding GetHashCode()?
public class Foo {
public Guid Id { get; } = new Guid();
public bool Equal(Foo other) {
if (other == null) return false;
return Id == other.Id;
}
}
Your code looks like you want to implement IEquatable<Foo> for your object. And MSDN advice to oveeride Object.Equals() method:
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.
Another pointer to your solution can be found here - It explains what all are your options -- please have a read
Typically, you implement value equality when objects of the type are
expected to be added to a collection of some sort, or when their
primary purpose is to store a set of fields or properties. You can
base your definition of value equality on a comparison of all the
fields and properties in the type, or you can base the definition on a
subset.

How does the default comparator work in C#?

I'm using OrderBy for some sorting based on properties and I found the documentation for the default comparer but it didn't explain much to me. If an object doesn't implement System.IComparable<T>, how does it generate a Comparer<T>?
For instance, I'm currently sorting a list of objects based a property value of type object. They are numeric types underneath and the sorting works fine. How does C#/Linq know how to sort the object? Does it do some un-boxing into primitives? Does it do some hash checking? How would that translate into greater than or less than?
If they were a more complex type would this fail with an error or would OrderBy do nothing, or would it even sort in a way that made no sense?
Well you can check the reference source and see for yourself what it does.
public static Comparer<T> Default {
get {
Contract.Ensures(Contract.Result<Comparer<T>>() != null);
Comparer<T> comparer = defaultComparer;
if (comparer == null) {
comparer = CreateComparer();
defaultComparer = comparer;
}
return comparer;
}
}
private static Comparer<T> CreateComparer() {
RuntimeType t = (RuntimeType)typeof(T);
// If T implements IComparable<T> return a GenericComparer<T>
#if FEATURE_LEGACYNETCF
//(SNITP)
#endif
if (typeof(IComparable<T>).IsAssignableFrom(t)) {
return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t);
}
// If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), u);
}
}
// Otherwise return an ObjectComparer<T>
return new ObjectComparer<T>();
}
So what it does is it checks if the type implements IComparable<T>, if it does it uses the comparer built in to the type (your list of objects that are numeric types would follow this branch). It then does the same check again in case the type is a Nullable<ICompareable<T>>. If that also fails it uses the ObjectComparer which uses Comparer.Default.
Here is the Compare code for Comparer.Default
public int Compare(Object a, Object b) {
if (a == b) return 0;
if (a == null) return -1;
if (b == null) return 1;
if (m_compareInfo != null) {
String sa = a as String;
String sb = b as String;
if (sa != null && sb != null)
return m_compareInfo.Compare(sa, sb);
}
IComparable ia = a as IComparable;
if (ia != null)
return ia.CompareTo(b);
IComparable ib = b as IComparable;
if (ib != null)
return -ib.CompareTo(a);
throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
}
As you can see it checks if a or b implements IComparable and if neither do it throws a exception.
Browsing on Reference Source, it returns an ObjectComparer<T>, which is a special internal type that just delegates the work to System.Collections.Comparer.Default.
This, in turn, throws an exception if it receives parameters that do not implement IComparable. Since that comparer works through downcasting and reflection, then it does not care if the static type of the object does not implement IComparable (which is the case if you have a list of objects).
So the bottom line is this: first it checks for IComparable<T>, then it checks for IComparable, and finally it throws an exception.
By the way, most (I'd say all even) built-in types implement IComparable<T> in some way, so that's how they can be sorted.
int, or to be more precise, Int32 does actually implement IComparable, so it works. (source)
OrderBy appears to attempt to use the comparator for the first type it comes across, so if you start with an object that doesn't implement IComparable, you will get an ArgumentException:
At least one object must implement IComparable
If you start with say, an Int32, then you will get the same exception with:
Object must be of type Int32
From the comparer for Int32
Looking at the internals, if the object is generic and implements IComparable<T>, the default comparer will return a GenericComparer instance that casts objects to that interface to perform comparisons. Primitive types already implement it. Nullable primitive types automatically implement that interface as well, so the returned NullableComparer works similarly. There is no boxing/unboxing in those scenarios.
Otherwise, it will try to cast objects into non-generic IComparable instances which can cause boxing with structs or throw an ArgumentException if the type does not implement it.

Which IEqualityComparer is used in a Dictionary?

Lets say I instantiate a dictionary like this
var dictionary = new Dictionary<MyClass, SomeValue>();
And MyClass is my own class that implements an IEqualityComparer<>.
Now, when I do operations on the dictionary - such as Add, Contains, TryGetValue etc - does dictionary use the default EqualityComparer<T>.Default since I never passed one into the constructor or does it use the IEqualityComparer that MyClass implements?
Thanks
It will use the default equality comparer.
If an object is capable of comparing itself for equality with other objects then it should implement IEquatable, not IEqualityComparer. If a type implements IEquatable then that will be used as the implementation of EqualityCOmparer.Default, followed by the object.Equals and object.GetHashCode methods otherwise.
An IEqualityComparer is designed to compare other objects for equality, not itself.
It will use IEqualityComparer<T>.Default if you don't specify any equality comparer explicitly.
This default equality comparer will use the methods Equals and GetHashCode of your class.
Your key class should not implement IEqualityComparer, this interface should be implemented when you want to delegate equality comparisons to a different class. When you want the class itself to handle equality comparisons, just override Equals and GetHashCode (you can also implement IEquatable<T> but this is not strictly required).
If you want to use IEquatable<T> you can do so without having to create a separate class but you do need to implement GetHashCode().
It will pair up GetHashCode() and bool Equals(T other) and you don't have to use the archaic Equals signature.
// tested with Dictionary<T>
public class Animal : IEquatable<Animal>
{
public override int GetHashCode()
{
return (species + breed).GetHashCode();
}
public bool Equals(Animal other)
{
return other != null &&
(
this.species == other.species &&
this.breed == other.breed &&
this.color == other.color
);
}
}

When I compare an Object (type) does it uses the IEquatable of a specific class?

My method receives two parameters, both of Object type. They have the same type, that implements IEquatable.
My question is: when I do: param1 == param2 does the framework compare using the IEquatable operator override of specific class or does it uses the object.Equals that just compares the memory pointer of two objects?
Which is the best way to do this? Is with generics and derivation constraints?
Actually, it does neither. The == operator by default will test for reference equality, regardless of the overridden behavior of your Equals method (if you've overridden it, which you certainly should have if you implemented IEquatable<T>).
That is to say, if your variables are typed as object but you want to use your own custom equality comparison, use Equals(x, y) rather than x == y.
Then, even if you've implemented IEquatable<T>, be sure to still override object.Equals, like this:
class MyType : IEquatable<MyType>
{
public bool Equals(MyType other)
{
// Whatever you want.
}
public override bool Equals(object other)
{
// Presumably you check for null above.
return Equals(other as MyType);
}
}
While you certainly can also overload the == and != operators for your type, this won't accomplish anything if you have references to objects of this type that are simply object variables, like this:
object x = new MyType();
object y = new MyType();
Console.WriteLine(Equals(x, y));
Console.WriteLine(x == y);
The above won't work as you might expect (if you've overloaded == and expect that to be used) because the == overload has to be resolved at compile-time; since x and y are typed as arbitrary objects, the C# compiler will pick the object type's == operator, which, again, just tests for reference equality.
Update: Now, you can ensure your == operator is used if your variables are typed as the class wherein you defined it or a more derived type. For example, given the following types:
class A
{
public static bool operator ==(A x, A y) { return true; }
public static bool operator !=(A x, A b) { return false; }
}
class B : A { }
class AComparer<T> where T : A
{
public bool CompareEqual(T x, T y) { return x == y; }
}
The AComparer<T>.CompareEqual method above will use your overloaded == operator for any type T deriving from A.
The key thing to remember is that == is static, which means its overload resolution gets performed at compile-time, not at run-time using a vtable (unless you're using dynamic, but that's a whole other beast). So just be aware of that whenever you're using the == operator in code and you want the overload to resolve to that of your custom type.
You must override Equals too, and even GetHashCode(): have a look here:
http://blogs.msdn.com/b/jaredpar/archive/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object-s-equals-and-gethashcode.aspx
If your method's parameters are specified as object then doing param1 == param2 just performs reference equality, since the == operator isn't polymorphic.

What is "Best Practice" For Comparing Two Instances of a Reference Type?

I came across this recently, up until now I have been happily overriding the equality operator (==) and/or Equals method in order to see if two references types actually contained the same data (i.e. two different instances that look the same).
I have been using this even more since I have been getting more in to automated testing (comparing reference/expected data against that returned).
While looking over some of the coding standards guidelines in MSDN I came across an article that advises against it. Now I understand why the article is saying this (because they are not the same instance) but it does not answer the question:
What is the best way to compare two reference types?
Should we implement IComparable? (I have also seen mention that this should be reserved for value types only).
Is there some interface I don't know about?
Should we just roll our own?!
Many Thanks ^_^
Update
Looks like I had mis-read some of the documentation (it's been a long day) and overriding Equals may be the way to go..
If you are implementing reference
types, you should consider overriding
the Equals method on a reference type
if your type looks like a base type
such as a Point, String, BigNumber,
and so on. Most reference types should
not overload the equality operator,
even if they override Equals. However,
if you are implementing a reference
type that is intended to have value
semantics, such as a complex number
type, you should override the equality
operator.
Implementing equality in .NET correctly, efficiently and without code duplication is hard. Specifically, for reference types with value semantics (i.e. immutable types that treat equvialence as equality), you should implement the System.IEquatable<T> interface, and you should implement all the different operations (Equals, GetHashCode and ==, !=).
As an example, here’s a class implementing value equality:
class Point : IEquatable<Point> {
public int X { get; }
public int Y { get; }
public Point(int x = 0, int y = 0) { X = x; Y = y; }
public bool Equals(Point other) {
if (other is null) return false;
return X.Equals(other.X) && Y.Equals(other.Y);
}
public override bool Equals(object obj) => Equals(obj as Point);
public static bool operator ==(Point lhs, Point rhs) => object.Equals(lhs, rhs);
public static bool operator !=(Point lhs, Point rhs) => ! (lhs == rhs);
public override int GetHashCode() => X.GetHashCode() ^ Y.GetHashCode();
}
The only movable parts in the above code are the bolded parts: the second line in Equals(Point other) and the GetHashCode() method. The other code should remain unchanged.
For reference classes that do not represent immutable values, do not implement the operators == and !=. Instead, use their default meaning, which is to compare object identity.
The code intentionally equates even objects of a derived class type. Often, this might not be desirable because equality between the base class and derived classes is not well-defined. Unfortunately, .NET and the coding guidelines are not very clear here. The code that Resharper creates, posted in another answer, is susceptible to undesired behaviour in such cases because Equals(object x) and Equals(SecurableResourcePermission x) will treat this case differently.
In order to change this behaviour, an additional type check has to be inserted in the strongly-typed Equals method above:
public bool Equals(Point other) {
if (other is null) return false;
if (other.GetType() != GetType()) return false;
return X.Equals(other.X) && Y.Equals(other.Y);
}
It looks like you're coding in C#, which has a method called Equals that your class should implement, should you want to compare two objects using some other metric than "are these two pointers (because object handles are just that, pointers) to the same memory address?".
I grabbed some sample code from here:
class TwoDPoint : System.Object
{
public readonly int x, y;
public TwoDPoint(int x, int y) //constructor
{
this.x = x;
this.y = y;
}
public override bool Equals(System.Object obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// If parameter cannot be cast to Point return false.
TwoDPoint p = obj as TwoDPoint;
if ((System.Object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public bool Equals(TwoDPoint p)
{
// If parameter is null return false:
if ((object)p == null)
{
return false;
}
// Return true if the fields match:
return (x == p.x) && (y == p.y);
}
public override int GetHashCode()
{
return x ^ y;
}
}
Java has very similar mechanisms. The equals() method is part of the Object class, and your class overloads it if you want this type of functionality.
The reason overloading '==' can be a bad idea for objects is that, usually, you still want to be able to do the "are these the same pointer" comparisons. These are usually relied upon for, for instance, inserting an element into a list where no duplicates are allowed, and some of your framework stuff may not work if this operator is overloaded in a non-standard way.
Below I have summed up what you need to do when implementing IEquatable and provided the justification from the various MSDN documentation pages.
Summary
When testing for value equality is desired (such as when using objects in collections) you should implement the IEquatable interface, override Object.Equals, and GetHashCode for your class.
When testing for reference equality is desired you should use operator==,operator!= and Object.ReferenceEquals.
You should only override operator== and operator!= for ValueTypes and immutable reference types.
Justification
IEquatable
The System.IEquatable interface is used to compare two instances of an object for equality. The objects are compared based on the logic implemented in the class. The comparison results in a boolean value indicating if the objects are different. This is in contrast to the System.IComparable interface, which return an integer indicating how the object values are different.
The IEquatable interface declares two methods that must be overridden. The Equals method contains the implementation to perform the actual comparison and return true if the object values are equal, or false if they are not. The GetHashCode method should return a unique hash value that may be used to uniquely identify identical objects that contain different values. The type of hashing algorithm used is implementation-specific.
IEquatable.Equals Method
You should implement IEquatable for your objects to handle the possibility that they will be stored in an array or generic collection.
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
Guidelines for Overriding Equals() and Operator == (C# Programming Guide)
x.Equals(x) returns true.
x.Equals(y) returns the same value as y.Equals(x)
if (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true.
Successive invocations of x. Equals (y) return the same value as long as the objects referenced by x and y are not modified.
x. Equals (null) returns false (for non-nullable value types only. For more information, see Nullable Types (C# Programming Guide).)
The new implementation of Equals should not throw exceptions.
It is recommended that any class that overrides Equals also override Object.GetHashCode.
Is is recommended that in addition to implementing Equals(object), any class also implement Equals(type) for their own type, to enhance performance.
By default, the operator == tests for reference equality by determining whether two references indicate the same object. Therefore, reference types do not have to implement operator == in order to gain this functionality. When a type is immutable, that is, the data that is contained in the instance cannot be changed, overloading operator == to compare value equality instead of reference equality can be useful because, as immutable objects, they can be considered the same as long as they have the same value. It is not a good idea to override operator == in non-immutable types.
Overloaded operator == implementations should not throw exceptions.
Any type that overloads operator == should also overload operator !=.
== Operator (C# Reference)
For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise.
For reference types other than string, == returns true if its two operands refer to the same object.
For the string type, == compares the values of the strings.
When testing for null using == comparisons within your operator== overrides, make sure you use the base object class operator. If you don't, infinite recursion will occur resulting in a stackoverflow.
Object.Equals Method (Object)
If your programming language supports operator overloading and if you choose to overload the equality operator for a given type, that type must override the Equals method. Such implementations of the Equals method must return the same results as the equality operator
The following guidelines are for implementing a value type:
Consider overriding Equals to gain increased performance over that provided by the default implementation of Equals on ValueType.
If you override Equals and the language supports operator overloading, you must overload the equality operator for your value type.
The following guidelines are for implementing a reference type:
Consider overriding Equals on a reference type if the semantics of the type are based on the fact that the type represents some value(s).
Most reference types must not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you must override the equality operator.
Additional Gotchas
When overriding GetHashCode() make sure you test reference types for NULL before using them in the hash code.
I ran into a problem with interface-based programming and operator overloading described here: Operator Overloading with Interface-Based Programming in C#
That article just recommends against overriding the equality operator (for reference types), not against overriding Equals. You should override Equals within your object (reference or value) if equality checks will mean something more than reference checks. If you want an interface, you can also implement IEquatable (used by generic collections). If you do implement IEquatable, however, you should also override equals, as the IEquatable remarks section states:
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. This ensures that all invocations of the Equals method return consistent results.
In regards to whether you should implement Equals and/or the equality operator:
From Implementing the Equals Method
Most reference types should not overload the equality operator, even if they override Equals.
From Guidelines for Implementing Equals and the Equality Operator (==)
Override the Equals method whenever you implement the equality operator (==), and make them do the same thing.
This only says that you need to override Equals whenever you implement the equality operator. It does not say that you need to override the equality operator when you override Equals.
For complex objects that will yield specific comparisons then implementing IComparable and defining the comparison in the Compare methods is a good implementation.
For example we have "Vehicle" objects where the only difference may be the registration number and we use this to compare to ensure that the expected value returned in testing is the one we want.
I tend to use what Resharper automatically makes. for example, it autocreated this for one of my reference types:
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.GetType() == typeof(SecurableResourcePermission) && Equals((SecurableResourcePermission)obj);
}
public bool Equals(SecurableResourcePermission obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return obj.ResourceUid == ResourceUid && Equals(obj.ActionCode, ActionCode) && Equals(obj.AllowDeny, AllowDeny);
}
public override int GetHashCode()
{
unchecked
{
int result = (int)ResourceUid;
result = (result * 397) ^ (ActionCode != null ? ActionCode.GetHashCode() : 0);
result = (result * 397) ^ AllowDeny.GetHashCode();
return result;
}
}
If you want to override == and still do ref checks, you can still use Object.ReferenceEquals.
Microsoft appears to have changed their tune, or at least there is conflicting info about not overloading the equality operator. According to this Microsoft article titled How to: Define Value Equality for a Type:
"The == and != operators can be used with classes even if the class does not overload them. However, the default behavior is to perform a reference equality check. In a class, if you overload the Equals method, you should overload the == and != operators, but it is not required."
According to Eric Lippert in his answer to a question I asked about Minimal code for equality in C# - he says:
"The danger you run into here is that you get an == operator defined for you that does reference equality by default. You could easily end up in a situation where an overloaded Equals method does value equality and == does reference equality, and then you accidentally use reference equality on not-reference-equal things that are value-equal. This is an error-prone practice that is hard to spot by human code review.
A couple years ago I worked on a static analysis algorithm to statistically detect this situation, and we found a defect rate of about two instances per million lines of code across all codebases we studied. When considering just codebases which had somewhere overridden Equals, the defect rate was obviously considerably higher!
Moreover, consider the costs vs the risks. If you already have implementations of IComparable then writing all the operators is trivial one-liners that will not have bugs and will never be changed. It's the cheapest code you're ever going to write. If given the choice between the fixed cost of writing and testing a dozen tiny methods vs the unbounded cost of finding and fixing a hard-to-see bug where reference equality is used instead of value equality, I know which one I would pick."
The .NET Framework will not ever use == or != with any type that you write. But, the danger is what would happen if someone else does. So, if the class is for a 3rd party, then I would always provide the == and != operators. If the class is only intended to be used internally by the group, I would still probably implement the == and != operators.
I would only implement the <, <=, >, and >= operators if IComparable was implemented. IComparable should only be implemented if the type needs to support ordering - like when sorting or being used in an ordered generic container like SortedSet.
If the group or company had a policy in place to not ever implement the == and != operators - then I would of course follow that policy. If such a policy were in place, then it would be wise to enforce it with a Q/A code analysis tool that flags any occurrence of the == and != operators when used with a reference type.
I believe getting something as simple as checking objects for equality correct is a bit tricky with .NET's design.
For Struct
1) Implement IEquatable<T>. It improves performance noticeably.
2) Since you're having your own Equals now, override GetHashCode, and to be consistent with various equality checking override object.Equals as well.
3) Overloading == and != operators need not be religiously done since the compiler will warn if you unintentionally equate a struct with another with a == or !=, but its good to do so to be consistent with Equals methods.
public struct Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
if (obj == null || !(obj is Entity))
return false;
return Equals((Entity)obj);
}
public static bool operator ==(Entity e1, Entity e2)
{
return e1.Equals(e2);
}
public static bool operator !=(Entity e1, Entity e2)
{
return !(e1 == e2);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
For Class
From MS:
Most reference types should not overload the equality operator, even if they override Equals.
To me == feels like value equality, more like a syntactic sugar for Equals method. Writing a == b is much more intuitive than writing a.Equals(b). Rarely we'll need to check reference equality. In abstract levels dealing with logical representations of physical objects this is not something we would need to check. I think having different semantics for == and Equals can actually be confusing. I believe it should have been == for value equality and Equals for reference (or a better name like IsSameAs) equality in the first place. I would love to not take MS guideline seriously here, not just because it isn't natural to me, but also because overloading == doesn't do any major harm. That's unlike not overriding non-generic Equals or GetHashCode which can bite back, because framework doesn't use == anywhere but only if we ourself use it. The only real benefit I gain from not overloading == and != will be the consistency with design of the entire framework over which I have no control of. And that's indeed a big thing, so sadly I will stick to it.
With reference semantics (mutable objects)
1) Override Equals and GetHashCode.
2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.
public class Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(null, other))
return false;
//if your below implementation will involve objects of derived classes, then do a
//GetType == other.GetType comparison
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
return Equals(obj as Entity);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
With value semantics (immutable objects)
This is the tricky part. Can get easily messed up if not taken care..
1) Override Equals and GetHashCode.
2) Overload == and != to match Equals. Make sure it works for nulls.
2) Implementing IEquatable<T> isn't a must, but will be nice if you have one.
public class Entity : IEquatable<Entity>
{
public bool Equals(Entity other)
{
if (ReferenceEquals(this, other))
return true;
if (ReferenceEquals(null, other))
return false;
//if your below implementation will involve objects of derived classes, then do a
//GetType == other.GetType comparison
throw new NotImplementedException("Your equality check here...");
}
public override bool Equals(object obj)
{
return Equals(obj as Entity);
}
public static bool operator ==(Entity e1, Entity e2)
{
if (ReferenceEquals(e1, null))
return ReferenceEquals(e2, null);
return e1.Equals(e2);
}
public static bool operator !=(Entity e1, Entity e2)
{
return !(e1 == e2);
}
public override int GetHashCode()
{
throw new NotImplementedException("Your lightweight hashing algorithm, consistent with Equals method, here...");
}
}
Take special care to see how it should fare if your class can be inherited, in such cases you will have to determine if a base class object can be equal to a derived class object. Ideally, if no objects of derived class is used for equality checking, then a base class instance can be equal to a derived class instance and in such cases, there is no need to check Type equality in generic Equals of base class.
In general take care not to duplicate code. I could have made a generic abstract base class (IEqualizable<T> or so) as a template to allow re-use easier, but sadly in C# that stops me from deriving from additional classes.
All the answers above do not consider polymorphism, often you want derived references to use the derived Equals even when compared via a base reference. Please see the question/ discussion/ answers here - Equality and polymorphism

Categories