Comparing Values for Equality in .NET: Identity and Equivalence (Part 3)

Continued from part 1 and part 2:

Care with == and Reference Types

One final thing to note is that operator overloads dont behave like overrides. If you use the == operator with reference types without thinking this can be a problem.
For example, suppose you have an untyped DataSet ds containing a DataTable dt. Suppose this has columns Id and Name. dt has two rows. Consider the following code:

            // Create DataSet

            DataSet ds = new DataSet("ds");

            DataTable dt = ds.Tables.Add("dt");

            dt.Columns.Add("Value", typeof(int));

 

            // Add two rows, both with Value column set to 1

            DataRow row1 = dt.NewRow(); row1["Value"] = 1; dt.Rows.Add(row1);

            DataRow row2 = dt.NewRow(); row2["Value"] = 1; dt.Rows.Add(row2);

            Console.WriteLine(row1["Value"] == row2["Value"]);       // Compare with == returns false.

            Console.WriteLine(row1["Value"].Equals(row2["Value"]));  // Compare with .Equals returns true.

When we compare with == in the example above we get false, even though the column in both rows contains the integer 1. The reason is that both row1[Value] and row2[Value] return objects, not integers. So == will use the == in System.Object, not any overloaded version in integer. The == in System.Object does an identity comparison (reference equality test). The underlying values have been separately boxed onto the heap, so arent in the same memory address, and the test fails.

When we compare with .Equals we get true. This is because .Equals is overridden in System.Int32 to do a value comparison, so the comparison uses the overridden version to correctly compare the values of the two integers.

v) a is b

a is b: Overview

a is b isnt actually a test for object equality at all, although it looks like one. b here has to be a type name(so b would need to be a class name, for example). The operator tests whether object a is either of type b or can be cast to it without an exception being thrown. This is equivalent to TypeOf a Is b in VB.NET, which is a little clearer.

a is b: Value Types/Reference Types

The operator works in the same way for both value types and reference types.

a is b: Override (overload?) or not?

The operator cannot be overloaded (or overridden clearly).

The Final Twist: String Interning

On the basis of the above what should this do?

object a = “Hello World”;
object b = “Hello World”;
Console.WriteLine(a.Equals(b));
Console.WriteLine(a == b);

At first glance you might say that:
a) a and b are reference types containing strings (you would be right).
b) .Equals is overridden in the string class to do an equivalence (value) comparison, and the values are equal. So a.Equals(b) is true (you would still be right).
c) However, a == b is an overload and on the object type it does an identity comparison not a value comparison (you would still be right).
a) a and b are separate objects in memory so a == b is false (you would be wrong).

d) is actually wrong, but only because of an optimization in the CLR. The CLR keeps a list of all strings currently being used in an application in something called the intern pool. When a new string is set up in code the CLR checks the intern pool to see if the string is already in use. If so it will not allocate memory to the string again, but will re-use the existing memory. Hence a == b is true above.

You can prevent strings being interned by using a StringBuilder as below. In this case a.Equals(b) will be true, and a== b will be false, which is what youd expect:

object a = “Hello World”;
object b = new StringBuilder().Append(“Hello).Append(“World”).ToString();
Console.WriteLine(a.Equals(b));
Console.WriteLine(a == b);

VB.NET

This article has talked mainly about C#. However, the situation is similarly confusingin VB.NET. Because they are methods on System.Object VB.NET has methods a.Equals(b), object.Equals(a, b) and object.ReferenceEquals(a, b) which are the same as the methods described above.

VB.NET has no == operator, or any operator equivalent to it.

VB.NET additionally has the Is operator. This operators use in TypeOf a Is b statements was discussed under a is b: Overview above.

VB.NET: a Is b

The Is operator can also be used for identity (reference equality) comparisons on two reference types in VB.NET. However, unlike a.ReferenceEquals(b), which does the same thing for reference types, the Is operator cannot be used at all with value types. TheVisual Basic compiler will not compile code where either of a or b in the statement a Is b are value types.

References: Jeffrey Richter “Applied Microsoft .NET Framework Programming”
http://www.microsoft.com/mspress/books/sampchap/5353.aspx#SampleChapter

Interning strings
http://msdn2.microsoft.com/en-us/library/system.string.intern.aspx

When to overload ==
http://msdn2.microsoft.com/en-us/library/ms173147.aspx

Advertisements

2 thoughts on “Comparing Values for Equality in .NET: Identity and Equivalence (Part 3)

  1. To clarify a little bit.
    Comparing string variables executes string.op_Equality() that does equality compare.
    Comparing string as objects executes object.op_Equality() that does only a identity compare.

    object a = “Hello World”;
    object b = “Hello”; // obfuscate the optimizer
    b += ” World”;
    Console.WriteLine(a.Equals(b)); // true – object equality compare
    Console.WriteLine(a == b); // false – object identity compare

    string s1 = (string)a;
    string s2 = (string)b;
    Console.WriteLine(s1.Equals(s2)); // true – string equality compare
    Console.WriteLine(s1 == s2); // true – overloaded ==operator doing string equality compare

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s