The Major Consequences of Using the C# “as” keyword

I was recently trying to create my own "CaseInsensitiveString" class to behave as if character case did not matter in terms of comparison; this is easy enough, or at least it sounds like it is.

The first thing I did was override the Equals method to allow for case-insensitive comparison; easy enough. Next I added operator == and operator != for easier comparison syntax; easy enough.  I also wanted to make my class be assignable from a string, and have it be implicitly castable to a string; easy enough, just provide an implicit operator string(MyClass) and an implicit operator MyClass(string).  After righting some quick sanity unit-tests, a problem arose:  all of these assertions work in this unit test:

   1: var caseInsensitiveString = new CaseInsensitiveString("Test String");
   2: string @string = caseInsensitiveString;
   4: Assert.IsTrue(caseInsensitiveString.Equals(@string));
   5: Assert.IsTrue(caseInsensitiveString == @string);
   6: Assert.IsTrue(@string.Equals(caseInsensitiveString));
   7: Assert.IsTrue(@string == caseInsensitiveString);

However, line 7 will not work.  Why?

After a little research, I found out that nUnit's Assert.IsTrue method is invoking the following version of System.String.Equals:

bool Equals(System.Object obj);

Microsoft's implementation of this code is as follows:

   1: public bool Equals(System.Object obj)
   2: {
   3:     String str = obj as String;
   4:     if (str == null)
   5:     {
   6:         // exception will be thrown later for null this
   7:         if (this != null)
   8:             return false;
   9:     }
  11:     return EqualsHelper(this, str);
  12: }

Do you see the problem yet?  The "as" keyword, for whatever reason, does not invoke any casting operators!  I find it strange that an explicit cast would work in this instance, or even an implicit cast, but not the "as" keyword.

The moral of the story is if you are using implicit type conversion operators, use the "as" keyword only if you only want the object in question to be explicitly casted to the type you provide.  The “as” keyword works fine with inheritance chains (as well as the “is” keyword).  However, in implicit type conversion operators it will not allow other objects to cast to the target.