November 2009 - Posts

This piece of code results in a warning.

   1: class Test
   2: {
   3:     public static void Main()
   4:     {
   5:         object obj = "Senthil";
   6:         string myName = "Senthil";
   7:         Console.WriteLine(obj == myName);
   8:     }
   9: }

The compiler says “warning CS0252: Possible unintended reference comparison; to get a value comparison, cast the left hand side to type 'string'” on line 7. The compiler is right; it has no way of knowing that obj is actually a string. It infers the static type of obj to be System.Object, and if you’ve done any programming in C# at all, you’ll know that the default behavior of the == operator is reference comparison. This code is unintentionally comparing reference values of two local variables, and that’s what the compiler is warning about.

Yet try running the code, and you’ll see that it works correctly; it prints True. Is the compiler wrong?

No. The code appears to work fine because string interning masks the problem. The C# compiler figures that there are two constant strings in the program, and both are equal, so instead of creating two distinct string objects, it creates only one. With strings being immutable, reusing string objects for identical strings is safe. And that’s why this code works, both obj and myName refer to the same string object, which means reference comparison will succeed.

Now that we know why it’s working, it’s easy to prove that the compiler warning is right with a small code change.

   1: class Test
   2: {
   3:     public static void Main()
   4:     {
   5:         object obj = "Senthil";
   6:  
   7:         string myName1 = "Sen";
   8:         string myName2 = "thil";
   9:         string myName = myName1 + myName2;
  10:         Console.WriteLine(obj == myName);
  11:     }
  12: }
 
Constructing myName dynamically forces the compiler to create another string object; it cannot reuse the interned one. As you’d expect, this piece of code will print False.

Fixing the warning is a mere matter of casting obj to the correct type (string) and then doing the equality check.

   1: class Test
   2: {
   3:     public static void Main()
   4:     {
   5:         object obj = "Senthil";
   6:  
   7:         string myName1 = "Sen";
   8:         string myName2 = "thil";
   9:         string myName = myName1 + myName2;
  10:         Console.WriteLine(((string)obj) == myName);
  11:     }
  12: }

Or we could use the Equals method, which is virtual and won’t be fooled by the compile time type.

   1: class Test
   2: {
   3:     public static void Main()
   4:     {
   5:         object obj = "Senthil";
   6:  
   7:         string myName1 = "Sen";
   8:         string myName2 = "thil";
   9:         string myName = myName1 + myName2;
  10:         Console.WriteLine(obj.Equals(myName));
  11:     }
  12: }

Both of them print True, which is what we want.

Posted by Senthil | 1 comment(s)
Filed under: , ,