What's the difference between "as" and "cast" operators?

What's the difference between "as" and "cast" operators?

Rate This
  • Comments 36

Most people will tell you that the difference between "(Alpha) bravo" and "bravo as Alpha" is that the former throws an exception if the conversion fails, whereas the latter returns null. Though this is correct, and this is the most obvious difference, it's not the only difference. There are pitfalls to watch out for here.

First off, since the result of the "as" operator can be null, the resulting type has to be one that takes a null value: either a reference type or a nullable value type. You cannot do "as int", that doesn't make any sense. If the argument isn't an int, then what should the return value be? The type of the "as" expression is always the named type so it needs to be a type that can take a null.

Second, the cast operator, as I've discussed before, is a strange beast. It means two contradictory things: "check to see if this object really is of this type, throw if it is not" and "this object is not of the given type; find me an equivalent value that belongs to the given type". The latter meaning of the cast operator is not shared by the "as" operator. If you say

short s = (short)123;
int? i = s as int?;

then you're out of luck. The "as" operator will not make the representation-changing conversions from short to nullable int like the cast operator would. Similarly, if you have class Alpha and unrelated class Bravo, with a user-defined conversion from Bravo to Alpha, then "(Alpha) bravo" will run the user-defined conversion, but "bravo as Alpha" will not. The "as" operator only considers reference, boxing and unboxing conversions.

And finally, of course the use cases of the two operators are superficially similar, but semantically quite different. A cast communicates to the reader "I am certain that this conversion is legal and I am willing to take a runtime exception if I'm wrong". The "as" operator communicates "I don't know if this conversion is legal or not; we're going to give it a try and see how it goes".

  • tobi: A null can be cast with () to any reference type for which the code compiles. With as, it obviously returns null as well. If you are casting with () to a non-nullable value type, you'd get an exception.

  • I'm curious why "(foo as Bar).Baz()" is considered an anti-pattern.  Is it just because of the potential for a null-reference exception?

    No. You're looking at this too narrowly. -- Eric

    I've used this from time to time in combination with the "as" operator, for example:

    if (x is Foo) { (x as Foo).M(); }
    else if (x is Bar) { (x as Bar).N(); }
    else if (x is Baz) { (x as Baz).P(); }
    else { throw new NotImplementedException(); }

    Obviously it doesn't happen often, but if the base class doesn't have a discriminator property then it's kind of the only way to deal with class hierarchies.

    Given that we've already validated x to be a Foo, Bar, whatever, then there's no way to get a null reference inside the conditional. Same goes with a class that's expected to implement some, but not all, of some set of interfaces - just change (Foo, Bar, Baz) to (IFoo, IBar, IBaz).

    I suppose I could write it like:

    IFoo foo = x as IFoo;
    if (foo != null) { foo.M(); return; }
    IBar bar = x as IBar;
    if (bar != null) { bar.N(); return; }

    ...and so on, and I guess this is the most efficient and "correct" way, but even this oversimplified example gives me a headache to look at.  It's not about aesthetics, not really, it's just that the way it's structured doesn't even come close to expressing its real intent.  The first version clearly presents itself as "x is going to be exactly one of these things, and depending on which one it is, we have to do something different with it."

    And there's the broader problem right there. -- Eric

    Is there a better way that I'm not aware of?

    You're looking at the problem at the wrong level. The problem isn't that the result might be null. The problem is that the type check is needed in the first place. Let's make your example slightly less abstract:

    void Frob(Animal x) {
    if (x is Mammal) { (x as Dog).Bark(); }
    else if (x is Mammal) { (x as Mammal).Milk(); }
    else if (x is Butterfly) { (x as Baz).FlutterBy(); }
    else { throw new NotImplementedException(); }

    What do we conclude about this? I conclude that the author of this method has no clear idea what "Frob" means for any given Animal! The fact that you have an Animal but then need to do something special depending on whether its a Dog, Mammal or Butterfly indicates a design problem in the Frob method's class; Frob probably should actually be three separate methods. It's also deeply unfortunate that Frob provides a trap for the unwary caller. Frob's contract looks like "I can handle any animal", but clearly it cannot. Again, this is evidence that Frob needs to be several different methods.

    Another tack: notice that essentially what I'm doing here is simulating a virtual call on a non-existent Frob method on Animal. We could therefore also conclude that some virtual method is missing from Animal, again indicating a potential design problem, this time with the class hierarchy. But either way, something here is messed up. -- Eric

  • Eric -- I have to agree with your response to Aaron that this type of pattern is a potential design problem.  But often this situation is unavoidable.  Often we do not have the option to go back and add a virtual method to the base class and implement them in the sub classes.  Extending your Animal example, what do you do when you need to add a new method "void TagAndRelease()" but are unable to modify class Animal and all it's sub classes?  Each sub class needs to have a different implementation.

    With version 3.5 of C# and before, the only option I see is using the "is" and "as" operators with lots of "if" statements like others have discussed.  Does anyone know another way?

    With version 4.0 of C# the new "dynamic" functionality could be used if you create a specific method for each sub class.

    void TagAndRelease(Animal x) { TagAndReleaseSpecific((dynamic) x); }

    void TagAndReleaseSpecific(Dog x) { // Dog specific logic }

    void TagAndReleaseSpecific(Butterfly x) { // Bufferfly specific logic }

    etc...

  • I wanted to ask why as operator was implemented this way? Before reading this post, I was quite sure that as opeator behaves the same way as normal casting does, but instead of exception I get null.

    Using as operator only for types that may be null is also clear.  

    However, I don't understand why as operator "will not make the representation-changing conversions". I think most people expect that.

    Another great post!

  • CAST: Try to force something to (be something that it might already be) or (force it to be something similar), ..if not possible, throw an exception.

    AS: Declare something to be something, if it is not of type something, do not CAST it, and default to NULL.

    You could maybe compare CAST & AS to <T>.Parse() & <T>.TryParse() ..one throws an exception, the other defaults to a value.

  • > If anything, the direct cast should've been easier to write instead of the speculative cast via "as".

    I second (third?) that, though I'm not sure about which syntax I would actually prefer for that. Personally I've always liked ML/F# syntax of "(x : T).y", but that would conflict with operator?: so it is not an option. I also like functional cast syntax which Delphi uses (and where, I believe, C# got "as" from): "T(x).y", and, so far as I can see, it would not introduce any quiet changes to existing code, but that has the potential of being confused with instance creation - as many other languages, such as Python and F#, use it for that.

    I actually like VB operators for that, their names are really pretty clear. If you do an up/down/interface cast which you assert will succeed, you use DirectCast(). For the same where the cast may not succeed and you need to check, you use TryCast(). For conversions (where representation changes), you use CType(), where "C" stands for "change".

    > To rewrite this using as instead doesn't look quite so tidy. Maybe you could put the as operation and assignment inside the IF condition and then check the value returned by the assignment operator, but that doesn't look so nice.

    > With version 3.5 of C# and before, the only option I see is using the "is" and "as" operators with lots of "if" statements like others have discussed.  Does anyone know another way?

    From my observations, a "typeswitch" construct for C# is one of the more frequent feature suggestions on Connect. It would be nice to have that, as it'd also remove most of the need for is/as - if you can do a check and bind a result of that check to a scoped identifier all at once, that's usually all that is actually needed. That said, it's purely a convenience feature, and would probably be used less than, say, auto properties, so if Eric says that those have barely got in the language, I don't have high hopes for typeswitch.

    > How do the casting operators behave when they encounter a null reference?

    The semantics are pretty much common sense. Any cast to a reference type or to a nullable value type will return "null" - which, by the way, means that even if (T)x didn't throw, it doesn't mean that (x is T)==true, so you still need that null check. Unboxing to a non-nullable value type will throw.

    > I'm curious why "(foo as Bar).Baz()" is considered an anti-pattern.  Is it just because of the potential for a null-reference exception?

    It's because you're essentially saying two contradictory things in a single expression. When you use "as", you say "maybe this isn't really a Bar, so please don't throw right there, but tell me instead. When you use operator. on the result, you say "this is never null", which implies "this is always a Bar".

    From a pragmatic point of view, if a cast failed, I want to get InvalidCastException, and not NullReferenceException. The latter can happen for numerous reasons, which would complicate the investigation somewhat. The former unambiguously identifiers the real problem.

    Of course, it's a much bigger deal when people don't use the result of "as" without a null-check immediately, but when they pass it to some method (which passes it to another method, which raises an event which stores it in a property ...), and then somewhere down the line (and much later) some code tries to use that value as if it wasn't null, and dies. That kind of thing can be notoriously hard to debug.

  • Making sure all non-private methods throw ArgumentNullException whenever null isn't explicitly a sensible argument helps a lot in the null-propagation scenario as far as finding the problem goes. Of course it's still a lot better to immediately throw an exception, and infinitely better to immediately throw an exception that actually specifically tells you what's wrong.

  • I've noticed that my WPF code is full of the dreaded is/as (anti-)pattern. For one thing, there are objects all over the place: ValueConverters, events, DataContexts, and more, all deal just with objects. I frequently need to know "do I have a List? how about a List of strings? a Dictionary? a plain IEnumerable?" or "is this a textbox? a checkbox? a listbox?" and I don't see a cleaner way to avoid the is/as if-block.

  • @Gabe, the block itself is not a problem in the scenario which you describe. The problem is a double check. You should use "as" followed by a null check in typeswitch scenarios, not "is" followed by cast or 'as". If you use StyleCop, it will actually point that one out (and, IIRC, so will ReSharper).

  • Why can't the compiler - or the JIT - optimize that case? For a local variable, it's not like it will have changed between the is and the cast.

  • Some great comments here...

    @Aaron, as far as a "better way" it largely depends if you have any control over the class hierarchy. If you can add a "Dispatch" method to each class in the hierarchy, then the "Double Dispatch" design pattern can be applied. This is very effective when two class hierarchies need to interact in a manner specific to the actual instance within each hierarchy (since C# - like many other languages evaluates parameters for a match based on declared and not actual type).

    If you do NOT have any control, then a potential alternative [IMHO it is subjective if it os "better" or not] is to build the dispatch using reflection

  • DRBlaise:

    Liked your note on the usage of the «dynamic» keyword for dynamic method dispatching.

    Many times I have missed this type of method dispatching: I first fell in love with the LISP programming language (CLOS, defgeneric, defmethod...).

    With the dynamic cast one explicitly states: "I wan't dynamic method dispatching on this call; please choose the most specific method overload, at run-time, according to the specific type that appears in this variable".

    And with methods of more than one parameter, non-dynamic parameters can statically reduce the set of possible method overloads, while dynamically parameters finally choose one at run-time.

    Anyway, as tempting as it seems, and being a (somewhat) conscious programmer, I wonder whether the pattern should be used at all. How does it compare in speed to the normal already mentioned patterns? Would it be a sin to use it?

    If it is much slower, however, it does seem possible that the compiler could provide an optimization for such use (?).

    As long as it "looks" good, I'm almost sure a lot of people will be using it!

    Also, with this post, I realized that casts may do conversions, if needed, to achieve their goal (that of returning a value of (i.e. assignable to) the specified type, null, or throw).

  • Pavel: As Aaron noted, it is really uglies-up the code to stick a bunch of local variables and nested ifs just avoid the is/as. Consider this code:

    var formvalues = from e in form.Children select (e is CheckBox) ? (e as CheckBox).IsChecked : (e is ListBox) ? (e as ListBox).SelectedItem : (e is TextBox) ? (e as TextBox).Text : null;

    I don't think there's any way to do that without using is/as that's anywhere near as tidy. If there is, I'm certainly open to it.

  • Gabe: What's wrong with polymorphism?  Extend each class to implement an interface that provides GetFormValue().

  • @Gabe, if you want neat over fast, you can always write a typeswitch yourself using lambdas and method chaining. It's trivially done (searching on MSDN blogs should find you some implementations), and it looks much neater, but it's somewhat slow because of delegate virtual calls overhead.

Page 2 of 3 (36 items) 123