Equality is tricky

Following facts are definitely going into my “need to be memorized bucket”. I remember dealing with equality checking issue a year ago. I used == instead of Equals and produced the nasty bug. But then I just fixed it and went on. But today I stumbled onto equality intricacy again while I was reading Bill Wagner’s “Effective C#” book “Item 9: Understand the Relationships Among ReferenceEquals(), static Equals(), instance Equals(), and operator==”. Indeed there are many subtleties to be aware of while dealing with equality. First, we should never redefine first two static methods. Instance Equals() is used to define our own semantics of equality of objects. Sometimes we override == in value types for performance reason (if we don’t do this, reflection will be used to compare two value types on field to field basis).
First gotcha:
int v = 5;
bool areEqual = object.ReferenceEquals(v,v);

areEqual variable is false. ReferenceEquals is always false when we use it to check equality for value types. Boxing is the one to blame.
Second gotcha:
Static Equals() uses the instance Equals() method of the first argument to determine whether two objects are equal (that’s why this method should never be redefined).
Third gotcha:
We need some code to illustrate.

class Program
{
static void Main(string[] args)
{
A first = new A{SomeProp="one"};
A second = new A { SomeProp = "one" };
Console.WriteLine(first.Equals(second));
Console.WriteLine(first==second);
}

public class A
{
public string SomeProp { get; set; }
public override bool Equals(object other)
{
// check null:
// the this pointer is never null in C# methods.
if ( other == null)
return false;

if (object.ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
//commented out because the base version is
//provided by System.Object
//if (!base.Equals(other))
//return false
// Compare this type's contents here:
A castedOther = other as A;
return
this.SomeProp.Equals(castedOther.SomeProp);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}

The output is:

true
false

The reason is simple. We overrided Equals() and its checking is based on object’s state now. But operator == still follows the reference semantics(memory pointer).