I re-learned something seemingly trivial today.  The == operator is not the same as Equals (more specifically, the default implementation of the == operator).

I overrode Equals for one of my classes and couldn't figure out why my code wasn't working.  Then I realized that == performs a reference comparison, not a value comparison.  I will have to keep this in mind any time I am comparing objects.  Generally, I will want to use the Equals method.

When I first encountered the problem, I decided to implement my own version of the == (and !=) operator.  The new implementation would perform a value comparison.  I felt uncomfortable with this and decided to do a bit of research.  Microsoft recommends you only override immutable types in this manner.  An immutable type is any type that cannot be changed after instantiation.  My class is not immutable.  So, I decided to take the == implementation out.

I think we easily can be confused because, many built in value types implement value comparison with the == operator.  And to add further complication, you have to consider if a string is "interned".  About the only time you are dealing with interned strings is when you are using literals.  If you are retrieving a string from a database, then it won't be interned.

Take a look at the NUnit test below.  I purposefully did not use AreEqual, AreSame, AreNotSame because I wanted to demonstrate more clearly how the .net operators and methods work.

   1: [Test]
   2: public void TestValueComparison()
   3: {
   4:     int a = 1;
   5:     int b = 1;
   6:     Assert.IsTrue( a == b );
   7:     Assert.IsTrue( a.Equals( b ) );
   8:     Assert.IsFalse( Object.ReferenceEquals( a, b ) );
   9:     //Interned strings    
  10:     string x = "test";
  11:     string y = "test";
  12:     Assert.IsTrue( x == y );
  13:     Assert.IsTrue( x.Equals( y ) );
  14:     Assert.IsTrue( Object.ReferenceEquals( x, y ) );
  15:     //Non-interned strings    
  16:     string p = new string( 'a', 3 );
  17:     string q = new string( 'a', 3 );
  18:     Assert.IsTrue( p == q );
  19:     Assert.IsTrue( p.Equals( q ) );
  20:     Assert.IsFalse( Object.ReferenceEquals( p, q ) );
  21:     //Manually interning the strings    
  22:     p = string.Intern( p );
  23:     q = string.Intern( q );
  24:     Assert.IsTrue( Object.ReferenceEquals( p, q ) );
  25: }

So, generally speaking, if you want to perform a value comparison of reference types, you want to use the Equals method.


posted on Thursday, May 29, 2008 10:38 AM
Filed Under [ .Net Design C# ]


# re: .Net Equality
posted by Mark Hildreth
on 6/4/2008 3:53 AM
Hi Will.

I've become a big fan of overriding equality, myself. You brought up a good point regarding only doing the == and != for immutable types, though. The thing that someone needs to watch out for with mutable types isn't so much equality, as it is GetHashCode(), which .NET warns you to override as well. If without thinking you use some properties in your GetHashCode, and one of those properties is changed, your GetHashCode changes. Any structure that depends on GetHashCode() (like dictionaries) fails.

This post describes it well...

# re: .Net Equality
posted by Will Smith
on 6/4/2008 11:05 PM
I didn't even mention GetHashCode because its second nature to implement it when I implement Equals. I am glad Mark mentioned it though. I can still remember when I asked myself why Microsoft added that infernal compiler warning.

Nice reference too, Mark. Thanks.

Post A Comment