To “as” or not to “as”

Iris Classon is asking and finding answers for “stupid” questions on her blog. First of all, they are definitely not stupid questions. There are a lot of nuances of .NET that slip out of our daily thinking. When I first started my Getting Geeky with the .NET Framework series I’d ask really hot coders basic questions – things that affect how code runs – and the results were not stellar. So, first, three cheers for Iris’s project.

Today’s question was “How should I do casting in C#? Should I use the prefix cast or the as-cast?” She’s got some good input that I suggest you read first. I also felt that the discussion left out a significant factor – debugging.

If the value resulting from the cast really should not be null, the code becomes unstable or incorrect at the point of the cast. Period. With the prefix cast, application state clearly becomes incorrect at that point because an exception occurs. With the “as” cast, the application state still becomes incorrect at that point, you just don’t know it. If the value should not be null, you’ll get a null exception, or possibly failure of a Requires clause if you’re using code contracts somewhere, sometime later in application execution.

One of the things that makes hard bugs hard is when there is a disconnect in time or space between the cause and the symptom. Time is time, space is lines of code, assembly placement, etc. Code can be written to minimize these disconnects. One of the ways to do that is to fail quickly. When application state becomes incorrect, yell about it immediately and rarely continue with the application in an invalid state. A null-reference exception at an unexpected time just makes code more difficult to debug.

Eric Lippert’s quote short-cutted the idea of what happens next. He’s quoted as saying that a prefixing cast failure requests, “then please crash the program.” That would be much better put as “then please do not continue the current operation.” Our programs need never crash in the sense of blue screen ugly death. Exception handling is a mechanism for recovering to a valid state, with the capacity to communicate failure to interested parties like the user and system logs.

So, use the “as” cast only when the resulting null does not make the application state incorrect, or you have an immediate and adjacent test that you prefer to make the application state correct or perform different exception management. For example, you might want to add a specific error message if the invalid cast indicates a particular type of problem.

I think the prefixing cast should be the “go to” cast for .NET – the one you use when you haven’t thought through the implications of the null value to the remainder of your program.

6 thoughts on “To “as” or not to “as””

  1. Completely agree, It always surprises me how many people defend “as” and how weak their reasoning usually is.
    Personally I like the idea of for each as have a ?? It’s a nice way of making sure you’ve thought about the case and provide a good default instead of just null.
    var str = myObject as string ?? string.Empty;

    You could even argue that something like
    var str = myObject as string ?? null;
    is a good idea as it forces you to explicitly accept the null and brings attention to the fact it can be null.

    And further if this was really embraced by .NET it could be used with stucts…
    var foo = bar as int ?? 0;

  2. Quite right! and a good comment from Chris. I think I’d be stricter on

    “or you have an immediate and adjacent test that you prefer to make the application state correct or perform different exception management”

    however, as code is always at risk of relocation during refactoring — and I wouldn’t want to complicate refactoring by having to think about the implied need for a local check.

  3. Chris,

    Is you’re suggestion to use

    var str = myObject as string ?? null;

    because not enough people grok the implication of “as”, or because the “as” keyword is to subtle for something more important.

    It’s an interesting suggestion, and I’m wondering if it’s for code in teams where people might not understand the effect, or for code in teams that understand this point, but might just read right past the implication in a particular piece of code.


  4. Tim,

    That’s a really good point. I think of adjacent code in terms of debugging, but refactoring is affected even more. Especially since side cases are sometimes overlooked in unit testing, and unit testing is the safety net for refactoring.

    But, by “immediate and adjacent”, I mean code that looks something like:

    var list = myList As List
    if (list == null)
    { throw new InvalidCastException(“special notes”); }

    If someone misses that on refactoring, are the paying dangerously insufficient attention to the code?


  5. I guess I was seeing it in two ways. First it would help people understand or remember that’s it can be null. Far too many times I’ve seen somebody use “as” and then immediately without any checks perform operations on it that could result in a null reference.

    The other case is just consistency. I guess in my ideal world, the language spec would state that an as MUST be followed by a ??.

    With that said, the few times I use an as without specifying a default, I don’t say “?? null” but I’d be willing to accept that for the extra safety. But even without compiler enforcing it, it could be a good idea.

    On another note, why do people love “if (something != null)”? To me “if (something is string)” reads much better. Admittedly we REALLY need an “is not” because “if (!(something is string))” is awful

  6. Perhaps a bit of a tangent but in reading Jon Skeet’s “C# in Depth”, he mentioned on page 121 that he did some timing studies where he had an object array where 1/3 of the objects were ints while the rest weren’t. He stated that iterating through the array and using the “is” operator plus an explicit cast was faster than using the “as” operator.

    That seemed counter-intuitive to me but, in fact, after running some tests of my own with the same distribution of ints versus non-ints in the array, I verified that on average my “as” runs took 14x as long as the “is plus cast” runs. I thought this was interesting and it certainly wasn’t what I expected.

Leave a Reply

Your email address will not be published. Required fields are marked *