Freigeben über


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

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".

Comments

  • Anonymous
    October 08, 2009
    Very interesting post as always. I did some testing and it also appears the "as" operator handles one more special case: Converting from a ValueType to Nullable<T>.  (Or is this considered a boxing and unboxing conversion)  The code below worked for me: int i = 1; int? n = i as int?;

  • Anonymous
    October 08, 2009
    The comment has been removed

  • Anonymous
    October 08, 2009
    The comment has been removed

  • Anonymous
    October 08, 2009
    The comment has been removed

  • Anonymous
    October 08, 2009
    @TheCPUWizard... You are quite correct, but if you start with... if (o is SomeClass) {     SomeClass sc = o as SomeClass;     /* Use sc / } else if (o is SomeOtherClass) {     SomeOtherClass soc = o as SomeOtherClass;    / Use soc / } else / etc */ 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. Besides, I would hope that a good compiler would optimise is-followed-by-as into a single operation anyway. No idea if any do.

  • Anonymous
    October 08, 2009
    @Pavel Minaev [MSFT] Totally agree. I kinda wish C# could do (X castas Y) to be exactly equivilent to ((Y)X), but that would mean a new reserved word.

  • Anonymous
    October 08, 2009
    The 'as' operator seems to be one of the most abused operators at my workplace. When asked, most devs say they use it "because it looks better." What often results, however, is that improper casts aren't caught until they try to use the null reference later on in the code. Consider this example: var someObject = (object)new SomeType(); <bunch of code> var A = someObject as ADifferentType; var B = (ADifferentType)someObject; <whole bunch of code> <whole bunch of code> <whole bunch of code> A.DoSomething(); B.DoSomething(); Assume here that ADifferentType does NOT derive from SomeType. In this example, the 'as' operator sets A to null because it's not the proper type. However you don't catch that until much later in the code (and in the extreme case, in an entirely different method), where you try to reference it. This makes debugging difficult. On the other hand, when we try to direct cast to variable B, we get an exception immediately and our bug is pinpointed on the spot. In the world of generics (and therefore mostly type safe code), I find the 'as' operator is almost never appropriate. I have a standing rule with my devs that if they find themselves tempted to use 'as', come down and see me because, except for a few rare circumstances, it almost always indicates an architectural problem.

  • Anonymous
    October 08, 2009
    @Bill.... As of 2010 Beta 1, the compiler (neither C# nor JIT) perofrms that type of operation, and these can be long running operations. Your code effectively is: if ((o as SomeClass) != null) {    SomeClass sc = o as SomeClass; } Would you EVER code that directly??? @Mike...If you consider the use of RTTI (Runtime Type Identification) based logic to be an "architectural problem", then I agree with your post. However I consider it (RTTI) to be avaluable tool.  If you are going to "restrict" the use of "as", then IMPO, to be consistant you must place the EXACT same restraints on "is".

  • Anonymous
    October 08, 2009
    @TheCPUWizard "As of 2010 Beta 1, the compiler (neither C# nor JIT) perofrms that type of operation," (Assuming "that type of operation" is refering to compilers optimising is-then-as.) That's a pity I think. It seems a simple optimisation, but I've never written a compiler so I can't really say. The fact we're having this conversation at all would indicate its a common enough thing that programmers do. "Would you EVER code that directly???" No, I wouldn't. I'd use the is operator instead. It looks simple enough with one if operation on its own, but I'm thinking more of a long else-if chain, and we only get the if-condition to do our thing. What's the alternative to my sample code in my earlier comment? I guess this... SomeClass sc; SomeOtherClass soc; /* 20 more. / if ((sc = o as SomeClass) != null) {   / Use sc /  } else if ((soc = o as SomeOtherClass) != null) {  / Use soc. /  } / 20 More. */ How much CPU time are we saving with this version? (vs the time spent going "what the .... is going on here?" when some tries to review my code.) If this (below) were legal, or the code were in a tight loop, I might be persuded. if ((SomeClass sc = o as SomeClass) != null) { }

  • Anonymous
    October 08, 2009
    @Bill - compare the situation to a method call. Multiple calls to the same method in sequence do not (and should not) be automatically reduced into a single one. As far as your second sample. I would NEVER code a chain like that. I would calculate the type ONCE and do a switch against constant values. As far as the timing goes, I recently was debugging code similar to the following: foreach (var o in coll) {   T t = o as T;   if (t != null)     t.Property = Value; } The "as" took 95% of the processing time (ie 20x the time taken to assign the property...

  • Anonymous
    October 08, 2009
    Not that you asked... I think of casts a mild code smell, much like comments. When I have one, I want to take a minute to ask whether there's another design that wouldn't need it, and whether that design is attractive in other ways. In C#, comments are easy to find, thanks to their unique and obvious syntax. If C# had chosen a syntax like C++'s dynamic_cast<> operator, it would be easy to find them, but it didn't. C# casts are not syntactically obvious, since parens are used for a lot of things.  At least 'is' and 'as' are easy to find, but casts are not, and it's too late to change the language to help. My wish (request?) is that the C# tools of the future make it easy to find casts, so I can give them that extra scrutiny.

  • Anonymous
    October 08, 2009
    The comment has been removed

  • Anonymous
    October 08, 2009
    The comment has been removed

  • Anonymous
    October 08, 2009
    @Simon Buchan: Given that there's Enumerable.Cast<T> in LINQ, I don't think it's a good idea to create another extension method with the same name. It won't be clear what's going on in all situations.

  • Anonymous
    October 08, 2009
    How do the casting operators behave when they encounter a null reference?

  • Anonymous
    October 09, 2009
    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.

  • Anonymous
    October 09, 2009
    The comment has been removed

  • Anonymous
    October 09, 2009
    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...

  • Anonymous
    October 09, 2009
    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!

  • Anonymous
    October 09, 2009
    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.

  • Anonymous
    October 09, 2009
    The comment has been removed

  • Anonymous
    October 09, 2009
    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.

  • Anonymous
    October 09, 2009
    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.

  • Anonymous
    October 10, 2009
    @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).

  • Anonymous
    October 10, 2009
    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.

  • Anonymous
    October 11, 2009
    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

  • Anonymous
    October 11, 2009
    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).

  • Anonymous
    October 11, 2009
    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.

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

  • Anonymous
    October 12, 2009
    @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.

  • Anonymous
    October 12, 2009
    Mark: Polymorphism is fine, but I didn't create the class hierarchy. Even if I subclass everything that I might need a value from, I can't get the rest of the world to use my subclasses; a DataGrid will always use a TextBox and there's no way to tell it to use my subclass; I have to interoperate with code that is already written, libraries I don't maintain. I'm sure you get the idea. Extension methods would be nice, but that only works if you know the types at compile-time. Pavel, compare these two options: 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; vs. var formvalues = from e in form.Children select new Select<object>(e).Case<CheckBox>(x => x.IsChecked).Case<ListBox>(x => x.SelectedItem).Case<TextBox>(x => x.Text).Default(null); Which one better expresses the intent of the code? Which one is easier to read? Which is easier to understand? I think the first one wins on all counts. In particular, the Select class is something I would have to write, which means it's 100+ lines of generic metaprogramming code that has to be maintained by my organization. Since the Select<>() idiom is not standard, you really have to study it to figure out what it does. And the whole reason we don't like the first option is the slight perf hit of having the redundant "is", so it doesn't make sense to replace the extra "is" with a method call, a delegate call, and all the other logic inside the Case functions. Not to mention that the first option stops executing the logic when it finds a matching type, while the second option still has to make a function call for each Case regardless of whether it does anything, plus it has the overhead of constructing a new Select instance for every child of the form.

  • Anonymous
    October 14, 2009
    The comment has been removed

  • Anonymous
    October 14, 2009
    The comment has been removed

  • Anonymous
    October 14, 2009
    Hello Eric, Do you see Gabe's code fragments? Do yo see what kind of monster are you creating? "var formvalues = from e in form.Children select new Select<object>(e).Case<CheckBox>(x => x.IsChecked).Case<ListBox>(x => x.SelectedItem).Case<TextBox>(x => x.Text).Default(null);" Ouch. How this will interfere with dynamic?

  • Anonymous
    June 24, 2010
    I'm new to C# and have been reading your blogs. They are very nice and informative. Thanks for them. I may be wrong but please clear my doubt. I have a question here: You have written in this blog: ["....The "as" operator will not make the representation-changing conversions from short to nullable int like the cast operator would....The "as" operator only considers reference, boxing and unboxing conversions."] This means that boxing and unboxing are not representation-changing conversions and so "as" handles them. Now, in another blog blogs.msdn.com/.../representation-and-identity.aspx you have mentioned that [".... Boxing and unboxing conversions are all representation-changing conversions...".] I find the two statements (in brackets above) from two different blogs contradicting each other. Kindly explain which one is correct?.. Please correct me if I've misunderstood them.

  • Anonymous
    December 05, 2011
    It's all pretty good, but you seem inclined to use as instead of a cast, so this isn't completly unbiased. The better option (at least for me) would be to use a method (if you use a try-catch or try-finally block, that surely won't fail).