Tuesday, February 10, 2009

operator== does reference comparison by default

Ran into an interesting topic today.  Resharper flags comparison between an object and a string using == as a potential problem.  You are likely going to be doing a reference comparison rather than a value comparison.

I debated this with a co-worker, and then did some tests and found that yes: using == between an object and a string will do a reference comparison, which could yield unexpected results.

Here is my NUnit test case:


[Test]
public void TestEquals()
{
string normalstring = "hello";
object normalobject = "hello";

// the two previous strings get recognized as the same value and are collapsed
// into the same reference.
Assert.IsTrue(ReferenceEquals(normalstring, normalobject), "ReferenceEquals(str,obj)");
Assert.IsTrue(ReferenceEquals(normalobject, normalstring), "ReferenceEquals(obj,str)");

// These all pass because they refer to the same object
Assert.IsTrue(string.Equals(normalstring, normalobject), "String.Equals(str,obj)");
Assert.IsTrue(string.Equals(normalobject, normalstring), "String.Equals(obj,str)");
Assert.AreEqual(normalstring, normalobject, "AreEqual(str,obj)");
Assert.AreEqual(normalobject, normalstring, "AreEqual(obj,str)");
Assert.IsTrue(normalobject == normalstring, "operator== obj,str");
Assert.IsTrue(normalstring == normalobject, "operator== str,obj");


// force new instances of the string "hello":
string newstring = new string(new[] { 'h', 'e', 'l', 'l', 'o', });
object newobj = new string(new[]{'h','e','l','l','o',});

//First assert that value comparisons with these new variables are the same as the
//previous ones
Assert.AreEqual(normalobject, newobj, "AreEqual(obj,obj)");
Assert.AreEqual(normalobject, newstring, "AreEqual(obj,str)");
Assert.AreEqual(normalstring, newobj, "AreEqual(str,obj)");
Assert.AreEqual(normalstring, newstring, "AreEqual(str,str)");

// next verify that the references are not the same
Assert.IsFalse(ReferenceEquals(newstring, newobj), "ReferenceEquals(str,obj)");
Assert.IsFalse(ReferenceEquals(newobj, newstring), "ReferenceEquals(obj,str)");

// using the .Equals method correctly resolves the value comparison
Assert.IsTrue(string.Equals(newstring, newobj), "String.Equals(str,obj)");
Assert.IsTrue(string.Equals(newobj, newstring), "String.Equals(obj,str)");
Assert.AreEqual(newstring, newobj, "AreEqual(str,obj)");
Assert.AreEqual(newobj, newstring, "AreEqual(obj,str)");

// but == does a reference comparison!!!
Assert.IsFalse(newobj == newstring, "operator== obj,str");
Assert.IsFalse(newstring == newobj, "operator== str,obj");
}



(Actual text is in source)

As you can see by running this, the last two lines fail the comparison, because the == is doing a reference comparison instead of a value comparison.

1 comment:

JaHoVil said...

Nice test, well documented !
string is, as many other types, an object with methods and properties and not a basic type.
Therfore, to compare the caracters in the string, you have to use a methode like equals.
I was supprised the first time I use strings in java and I found it heavy.

Nice blog.
J, from France.