Iris Classon
Iris Classon - In Love with Code

‘Stupid’ Question 20: How should I do casting in C#? Should I use the prefix cast or the as-cast?

[To celebrate my first year of programming I will ask a ‘stupid’ questions daily on my blog for a year, to make sure I learn at least 365 new things during my second year as a developer]

I remember when we learned about casting in school. It was when we learned about the hashtable. My teacher took the trash bin and walked around and put things it (even grabbing a few personal items from the students,- just for laughs). As some of the students on the front row got to fish out some of the items from the bin giggling he explained casting to us.

Casting in C# - how should I do this magic?

We learned to use the classic cast, we learned both ways, but the prefix cast stuck better with me in the beginning. I don’t know when, but at some point I started noticing ‘as’ casting in code examples and immediately liked the syntax better- it was more readable, even if I was skeptic to the lack of parentheses that I had learned to love. So I have to admit, I started using it without really looking into what it meant. I just assumed it was the same. But then I started getting weird exceptions. I was getting the dreaded NullReferenceException from my methods. What do you mean null??

So I asked myself, what is the difference between these two types of casting, and when should I use them?

The classic cast or prefix cast throws an InvalidCastException if the cast is not valid and should be used when you are expecting the cast to succeed - which seems to be the main recommendation (the question being why would you expect the casting to fail?- and is this (‘as’) the best way to handle that?)

With the ‘as’ casting a null will be returned if the conversion fails,- so this means that you can ‘test’ a cast – but also that the type has to be nullable or be able to take a null. This cast should be used when you are not sure if the casting will succeed.

Like somebody put it on stackoverflow:

Use as when it’s valid for an object not to be of the type that you want, and you want to act differently if it is.

And to quote the famous Lippert:

I think I covered that in the article, but to sum up, here are two ways to think about it.

First, “as” means “I expect that sometimes this will not succeed, give me null in those cases”. A cast means “I expect that this will always succeed; if I am wrong, then please crash the program”.

Second, “as” means “I want to know what this object *really is*, not what it is convertible to by some representation-changing specially-defined conversion rule. A cast means “convert this thing using whatever crazy mechanism you need to do to make it work.”

Choose the one that matches the meaning you intend to implement. – Eric

Comments

Leave a comment below, or by email.
Kevin Major
8/12/2012 9:34:42 AM
Ah, good to know.  I have a few 'as' casts I should change. 
Anthony Trudeau
8/12/2012 10:34:16 AM
You asked a follow-up question to Julie Lerman on Twitter.  The question was, "I have a new question in RE to Q20- is the performance win with 'as' something that matters today?"  I hope you don't mind me answering.

The answer in most cases is no.  An explicit cast (you call prefix cast) compiles down to the castclass IL operation.  And a fast cast (as-cast) compiles down to the isinst IL operation.  The isinst operation is slightly faster.  The fast cast becomes the most performant when you're doing a lot of casts or there is a high likelihood of casting exceptions -- for example at a public interface where you cannot be certain of the type of a passed argument.

I generally base my decision on my expectations of my code.  Do I expect that received arguments are likely to be of an unexpected type?  If so, I use the fast cast.  But, if I have a reasonable expectation that my received argument is going to be castable then I'll use an explicit cast and provide exception handling.  Explicit casting is probably a good candidate if your code looks something like this:

void Foo(object a1) {
     BaseObj b = a1 as BaseObj;

     if (b != null) {
          // do something; omitted for brevity
     }
     else {
          throw new Exception("...");
          // or even
          // Trace.TraceError("...");
     }
}

You can achieve the same thing with this:

void Foo(object a1) {
     try {
          BaseObj b = (BaseObj)a1;
          // do something; omitted for brevity
     }
     catch (InvalidCastException)  {    // note: no exception object
          throw new Exception("...");
          // or even
          // Trace.TraceError("...");
     }
} 
Daniel Przybylski
8/12/2012 10:39:39 AM
Keep in mind that the 'is' syntax only works with reference or nullable types, so sometimes you're just stuck with (cast)primitive. 
Kathleen Dollard
8/12/2012 11:39:10 AM
Great question. I followed up at http://msmvps.com/blogs/kathleen/archive/2012/08/12/to-as-or-not-to-as.aspx 
Atul
8/12/2012 11:56:58 PM
I would say there is more to it than just the conversion. While the idea of conversion can fail sometimes is valid, I have rarely seen code that uses "as" to really mean that. Cast conversions is almost always used when we expect things to succeed. 

Using a direct cast can throw an exception and we know exception raising is a heavier operation since it unwinds the stack and takes the execution to one upper level. Checking for if (null) would be easier and will let the program flow more smoothly from that point on. 

So apart from the intended meaning, i would give more weightage of if i am willing to get exception or not. 
Vicpada
8/13/2012 12:16:48 AM
Nice post!
Perhaps adding a paragraph about Convert.ToXXX() and comparing that to "as" and direct casting would be great too. 
James Curran
8/13/2012 8:18:06 AM
There's an old saying which goes "Expect the best, but plan for the worst".  Specifically here, One should always expect that a cast will work, but plan for it to fail.  And, since I"M going to handle what to do in case of the cast failing, I always do a "as" cast, since I am ALWAYS going to test for null immediately afterwards.  (And, since about 95% of the time, a null value is equally bad as a non-null value of the wrong time, you can handle both checks with one if(). )

The one place where I do use explicit cases (which everyone here sort-of hints at without coming out and saying) is when trying to un-box a value type.  That can only be done with an explicit case. 


Last modified on 2012-08-11

comments powered by Disqus