358

In development blogs, online code examples and (recently) even a book, I keep stumbling about code like this:

var y = x as T;
y.SomeMethod();

or, even worse:

(x as T).SomeMethod();

That doesn't make sense to me. If you are sure that x is of type T, you should use a direct cast: (T)x. If you are not sure, you can use as but need to check for null before performing some operation. All that the above code does is to turn a (useful) InvalidCastException into a (useless) NullReferenceException.

Am I the only one who thinks that this a blatant abuse of the as keyword? Or did I miss something obvious and the above pattern actually makes sense?

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 57
    Would be funnier to see (kiss as S).SteveIsSuchA(); But I agree, its an abuse. – SwDevMan81 Jan 26 '10 at 14:02
  • 5
    It's much cooler than writing `((T)x).SomeMethod()`, isn't it? ;) (just kidding, you're right of course!) – Lucero Jan 26 '10 at 14:04
  • I kind of remember that a cast via "as" is faster than direct cast - anyway, without a null-check it is useless, as you stated. – Oliver Friedrich Jan 26 '10 at 14:06
  • You are absolutely right, and it bothers me just as much, but this is really more of a rant than a question, which just isn't what this site was designed for. Voting to close. – P Daddy Jan 26 '10 at 14:07
  • @BeowulfOF: According to my tests, `as` is only faster than a straight cast if it fails, which is to be expected. And even if it *were* faster, it's still wrong. – P Daddy Jan 26 '10 at 14:10
  • @P Daddy: It was both. Since I am mainly a VB.NET developer, the question ("Or did I miss something obvious and the above pattern actually makes sense?") was not a rhetorical one. – Heinzi Jan 26 '10 at 14:12
  • 8
    @P Daddy I disagree, perfectly good question (does this code pattern actually make sense), and very useful. +1 to the question and a frown to anyone who votes to close. – MarkJ Jan 26 '10 at 14:13
  • 9
    Lucerno is right, this coding pattern is induced by trying to avoid parentheses. Incurable after having been exposed to Lisp. – Hans Passant Jan 26 '10 at 14:13
  • 13
    Optimized code: `(f as T).SomeMethod()` ;) – MSalters Jan 26 '10 at 14:13
  • 1
    I'm just curious, is it documented anywhere that "as" under the hood just throws and buries an InvalidCastException? Just curious how you know that (not arguing with you, just curious....) – BFree Jan 26 '10 at 14:18
  • @BFree: It doesn't. What I meant was: If x is not of type T, `((T)x).SomeMethod()` will throw an InvalidCastException, which is more helpful than the NullReferenceException caused by `(x as T).SomeMethod()`. The NullReferenceException in the latter case is *not* caused by `as` but by the (attempted) method call. – Heinzi Jan 26 '10 at 14:21
  • @Heinzi and MarkJ: Well, for the record, I did upvote the question as well as vote to close. – P Daddy Jan 26 '10 at 14:24
  • Other interesting facts about casting vs. as by John Skeet here: http://stackoverflow.com/questions/496096/casting-vs-using-the-as-keyword-in-the-clr – Mikhail Poda Jul 21 '10 at 06:50
  • 'as' lets you cast and then test for null. static cast requires a type test then cast. For inline casts 'as' is more legible imo, hence the popularity. Nested parenthesis is always harder to read. – Gusdor Sep 11 '13 at 08:48
  • `T a = (T)x` = left cast, `(x as T).SomeMethod();` = right cast. I'm happy with that and the code is cleaner from too many brackets. btw `if (x is T) (x as T).SomeMethod();` common lines in my code since I can copy if condition (with brackets), change i to a and use it for a method call. – Bitterblue Mar 21 '14 at 07:46
  • 1
    One valid but remote reason to write `(x as T).SomeMethod()` exists if SomeMethod is an extension method that sensibly deals with the argument being null and if another extension method exists with signature `SomeMethod(this U u)`, where `U` the type of the variable `x`. In this case the code writer could have intended to use the extension method that the compiler normally wouldn't choose. Of course, a cast would do as well. But in this peculiar case, I'd be fine with using `as` too. – JBSnorro Mar 21 '15 at 21:59
  • @Lucero - I take your joke seriously - I really think it is more readable to use `(x as T).SomeMethod()` to avoid those extra parentheses, and it just communicates that I *know* that x is T like Eric Lippert wrote on this post - so why is this so wrong? – BornToCode Aug 31 '16 at 11:54
  • @MSalters optimized null-safe code: `(x as T)?.SomeMethod();` – Victor Aug 19 '19 at 18:01

13 Answers13

252

Your understanding is true. That sounds like trying to micro-optimize to me. You should use a normal cast when you are sure of the type. Besides generating a more sensible exception, it also fails fast. If you're wrong about your assumption about the type, your program will fail immediately and you'll be able to see the cause of failure immediately rather than waiting for a NullReferenceException or ArgumentNullException or even a logical error sometime in the future. In general, an as expression that's not followed by a null check somewhere is a code smell.

On the other hand, if you are not sure about the cast and expect it to fail, you should use as instead of a normal cast wrapped with a try-catch block. Moreover, use of as is recommended over a type check followed by a cast. Instead of:

if (x is SomeType)
   ((SomeType)x).SomeMethod();

which generates an isinst instruction for the is keyword, and a castclass instruction for the cast (effectively performing the cast twice), you should use:

var v = x as SomeType;
if (v != null)
    v.SomeMethod();

This only generates an isinst instruction. The former method has a potential flaw in multithreaded applications as a race condition might cause the variable to change its type after the is check succeeded and fail at the cast line. The latter method is not prone to this error.


The following solution is not recommended for use in production code. If you really hate such a fundamental construct in C#, you might consider switching to VB or some other language.

In case one desperately hates the cast syntax, he/she can write an extension method to mimic the cast:

public static T To<T>(this object o) { // Name it as you like: As, Cast, To, ...
    return (T)o;
}

and use a neat[?] syntax:

obj.To<SomeType>().SomeMethod()
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 5
    I think the race condition is irrelevant. If you are experiencing this problem, then your code is not thread-safe, and there are more reliable ways to solve it than by using keyword "as". +1 for the rest of the answer. – RMorrisey Jan 26 '10 at 23:54
  • +1 for @RMorrisey. If the above code had a race condition you almost certainly have *serious* problems. Otherwise, one vs two bytecodes in a "compiled language" probably isn't going to be an issue! – Tom Hawtin - tackline Jan 27 '10 at 06:25
  • 10
    @RMorrisey: I have at least one example in mind: Assume you have a `cache` object that another thread tries to invalidate it by setting it to `null`. In lock-free scenarios, these kind of stuff can arise. – Mehrdad Afshari Jan 27 '10 at 09:55
  • 10
    is+cast is enough to trigger a "Do not cast unnecessarily" warning from FxCop: http://msdn.microsoft.com/en-us/library/ms182271.aspx That should be enough reason to avoid the construct. – David Schmitt Jan 27 '10 at 16:47
  • Point taken. I can see how it's an improvement in that scenario. In my coding I haven't had occasion/need to work without using locks. – RMorrisey Jan 27 '10 at 22:25
  • 2
    You should avoid making extension methods on `Object`. Use of the method on a value type will cause it to be boxed unnecessarily. – MgSam Nov 28 '12 at 06:24
  • 2
    @MgSam Obviously, such a use case does not make sense for the `To` method here, as it only converts across the inheritance hierarchy, which for value types involves boxing anyway. Of course, the entire idea is more theoretical than serious. – Mehrdad Afshari Nov 28 '12 at 07:13
  • 1
    Sure. The `To<>` extension will not work with conversions between pre-defined value types, such as narrowing an integer type, an also not work for user-defined conversions. So it can only work with reference conversions, boxing conversions and unboxing conversions. Therefore it is no problem that `o` is a box. Either there was a box already which was going to be unboxed, or else a box was about to be created all the same. The same box is re-used, of course. However, usually extension methods for `object` are a code smell and makes noise in intellisense. – Jeppe Stig Nielsen Jan 31 '15 at 11:59
  • How do this relate to the `if (x is SomeType y) y.SomeMethod();` usage of the `is` ? – Artog Apr 10 '18 at 09:04
42

IMHO, as just make sense when combined with a null check:

var y = x as T;
if (y != null)
    y.SomeMethod();
Rubens Farias
  • 57,174
  • 8
  • 131
  • 162
40

Using 'as' does not apply user defined conversions while the cast will use them where appropriate. That can be an important difference in some cases.

Larry Fix
  • 653
  • 1
  • 5
  • 8
  • 5
    This is important to remember. Eric Lippert goes over that here: http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx – P Daddy Jan 26 '10 at 18:15
  • 5
    Nice comment, P! If your code depends on this distinction, though, I'd say there's a late-night debugging session in your future. – TrueWill Jan 26 '10 at 19:17
36

I wrote a bit about this here:

http://blogs.msdn.com/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx

I understand your point. And I agree with the thrust of it: that a cast operator communicates "I am sure that this object can be converted to that type, and I am willing to risk an exception if I'm wrong", whereas an "as" operator communicates "I am not sure that this object can be converted to that type; give me a null if I'm wrong".

However, there is a subtle difference. (x as T).Whatever() communicates "I know not just that x can be converted to a T, but moreover, that doing so involves only reference or unboxing conversions, and furthermore, that x is not null". That does communicate different information than ((T)x).Whatever(), and perhaps that is what the author of the code intends.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • 1
    I disagree with your speculative defense of the code's author in your last sentence. `((T)x).Whatever()` *also* communicates that `x` is not [intended to be] null, and I highly doubt that an author would typically care whether the conversion to `T` occurs with only reference or unboxing conversions, or if it requires a user-defined or representation-changing conversion. After all, if I define `public static explicit operator Foo(Bar b){}`, then it is clearly my intent that `Bar` be considered compatible with `Foo`. It's rare that I would want to avoid this conversion. – P Daddy Jan 26 '10 at 21:48
  • 4
    Well, perhaps *most* authors of code would not be making that subtle distinction. I personally might be, but if I was, I'd add a comment to that effect. – Eric Lippert Jan 26 '10 at 21:58
16

I've often seen references to this misleading article as evidence that "as" is faster than casting.

One of the more obvious misleading aspects of this article is the graphic, which does not indicate what is being measured: I suspect it's measuring failed casts (where "as" is obviously much faster as no exception is thrown).

If you take the time to do the measurements, then you'll see that casting is, as you'd expect, faster than "as" when the cast succeeds.

I suspect this may be one reason for "cargo cult" use of the as keyword instead of a cast.

Joe
  • 122,218
  • 32
  • 205
  • 338
  • 2
    Thanks for the link, that's very interesting. From how I understood the article, he *does* compare the non-exception case. Nevertheless, the article was written for .net 1.1, and the comments point out that this changed in .net 2.0: Performance is now almost equal, with prefix cast even being slightly faster. – Heinzi Jan 26 '10 at 14:42
  • 1
    The article does imply he is comparing the non-exception case, but I did some tests a long time ago and wasn't able to reproduce his claimed results, even with .NET 1.x. And since the article doesn't provide the code used to run the benchmark, it's impossible to say what's being compared. – Joe Jan 26 '10 at 14:59
  • "Cargo cult" - perfect. Check out "Cargo Cult Science Richard Feynman" for the full info. – Bob Denny May 02 '10 at 18:22
11

The direct cast needs a pair of parentheses more than the as keyword. So even in the case where you're 100 % sure what the type is, it reduces visual clutter.

Agreed on the exception thing, though. But at least for me, most uses of as boil down to check for null afterwards, which I find nicer than catching an exception.

Joey
  • 344,408
  • 85
  • 689
  • 683
8

99% of the time when I use "as" is when I'm not sure what's the actual object type

var x = obj as T;
if(x != null){
 //x was type T!
}

and I don't want to catch explicit cast exceptions nor make cast twice, using "is":

//I don't like this
if(obj is T){
  var x = (T)obj; 
}
Massimiliano
  • 16,770
  • 10
  • 69
  • 112
8

It's just because people like the way it looks, it's very readable.

Lets face it: the casting/conversion operator in C-like languages is pretty terrible, readability-wise. I would like it better if C# adopted either the Javascript syntax of:

object o = 1;
int i = int(o);

Or define a to operator, the casting equivalent of as:

object o = 1;
int i = o to int;
JulianR
  • 16,213
  • 5
  • 55
  • 85
5

People likeas so much because it makes them feel safe from exceptions... Like guarantee on a box. A guy puts a fancy guarantee on the box 'cause he wants you to feel all warm and toasty inside. You figure you put that little box under your pillow at night, the Guarantee Fairy might come down and leave a quarter, am I right Ted?

Back on topic... when using a direct cast, there is the possibility for an invalid cast exception. So people apply as as a blanket solution to all of their casting needs because as (by itself) will never throw an exception. But the funny thing about that, is in the example you gave (x as T).SomeMethod(); you are trading an invalid cast exception for a null reference exception. Which obfuscates the real problem when you see the exception.

I generally don't use as too much. I prefer the is test because to me, it appears more readable and makes more sense then trying a cast and checking for null.

Bob
  • 97,670
  • 29
  • 122
  • 130
  • 2
    "I prefer the is test " - "is" followed by a cast is of course slower than "as" followed by a test for null (just like "IDictionary.ContainsKey" followed by dereferencing using the indexer is slower than "IDictionary.TryGetValue"). But if you find it more readable, no doubt the difference is rarely significant. – Joe Jan 26 '10 at 14:38
  • The important statement in the middle part is how people apply `as` as a blanket solution because it makes them feel safe. – Bob Jan 26 '10 at 15:16
5

This has to be one of my top peeves.

Stroustrup's D&E and/or some blog post I cant find right now discusses the notion of a to operator which would address the point made by https://stackoverflow.com/users/73070/johannes-rossel (i.e., same syntax as as but with DirectCast semantics).

The reason this didnt get implemented is because a cast should cause pain and be ugly so you get pushed away from using it.

Pity that 'clever' programmers (often book authors (Juval Lowy IIRC)) step around this by abusing as in this fashion (C++ doesnt offer an as, probably for this reason).

Even VB has more consistency in having a uniform syntax that forces you to choose a TryCast or DirectCast and make up your mind!

Community
  • 1
  • 1
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • +1. You probably meant `DirectCast` *behavior*, not *syntax*. – Heinzi Jan 26 '10 at 14:58
  • @Heinzi: Ta for +1. Good point. Decided to be a smartarse and use `semantics` instead :P – Ruben Bartelink Jan 26 '10 at 15:03
  • Given that C# has no pretense of compatibility with C, C++, or Java, I find myself peeved at some of the things it borrows from those languages. It goes beyond "I know this is an X", and "I know this isn't an X, but can be represented as one", to "I know this isn't an X, and might not really be representable as one, but give me an X anyway." I could see usefulness for a `double-to-int` cast that would fail if the `double` didn't represent an exact value that could fit in an `Int32`, but having `(int)-1.5` yield -1 is simply ugly. – supercat Feb 18 '14 at 23:06
  • @supercat Yep, but language design aint easy though as we all know - look at the set of tradeoffs involved in C# nullables. The only known antidote is regular reading of C# in Depth editions as they come along :) Thankfully I'm more concerned with understanding F#'s nuances these days and it's a lot more sane around a lot of these matters. – Ruben Bartelink Feb 19 '14 at 09:36
  • @RubenBartelink: I'm not quite clear what exact problems the nullable types were supposed to solve, but I would think in most cases it would have been nicer to have a `MaybeValid` with two public fields `IsValid` and `Value` which code could do with as it sees fit. That would have allowed e.g. `MaybeValid TryGetValue(TKey key) { var ret = default(MaybeValid); ret.IsValid = dict.TryGetValue(key, out ret.Value); return ret; }`. Not only would that save at least two copy operations compared with `Nullable`, but it could also worth with *any* type `T`--not just classes. – supercat Feb 19 '14 at 19:56
  • @RubenBartelink: I suspect one of the most important things in language designs (actually any kind of design) is a willingness to retreat from simplifications in one aspect of a design which either create annoying complications in another, or create what would have been easily avoidable difficulties for consumers. Sometimes I think it's too easy for designers to become overly attached to something that appears simple, clean, and elegant but in fact simply prevents what should have been slight complications from being handled in a place they could be dealt with. Care to chat? – supercat Feb 19 '14 at 20:03
2

I believe that the as keyword could be thought of as a more elegant looking version of the dynamic_cast from C++.

Andrew Garrison
  • 6,977
  • 11
  • 50
  • 77
  • I'd say a straight cast in C# is more like `dynamic_cast` in C++. – P Daddy Jan 26 '10 at 14:16
  • i think the straight cast in C# is more equivalent to the static_cast in C++. – Andrew Garrison Jan 26 '10 at 15:57
  • 2
    @Ruben Bartelink: It only returns null with pointers. With references, which you should be using when possible, it throws `std::bad_cast`. – P Daddy Jan 26 '10 at 17:16
  • 1
    @Andrew Garrison: `static_cast` performs no runtime type check. There is no similar cast to this in C#. – P Daddy Jan 26 '10 at 17:18
  • Sadly, I didn't know you could even use the casts on references, as I have only ever used them on pointers, but P Daddy is absolutely correct! – Andrew Garrison Jan 26 '10 at 22:10
  • @P Daddy: Had forgotten that, you're 100% correct ([Here's the article I used to verify](http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=179)) – Ruben Bartelink Jan 27 '10 at 08:30
1

It's probably more popular for no technical reason but just because it's easier to read and more intuitive. (Not saying it makes it better just trying to answer the question)

Jla
  • 11,304
  • 14
  • 61
  • 84
1

One reason for using "as":

T t = obj as T;
 //some other thread changes obj to another type...
if (t != null) action(t); //still works

Instead of (bad code):

if (obj is T)
{
     //bang, some other thread changes obj to another type...
     action((T)obj); //InvalidCastException
}
Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Rauhotz
  • 7,914
  • 6
  • 40
  • 44
  • 2
    If you've got race conditons this uglym you've got bigger issues (but agree its a nice sample to go with the others so +1 – Ruben Bartelink Jan 26 '10 at 15:01
  • -1 as this perpetuates a fallacy. If other threads can be changing the type of obj, then you still have problems. The claim "//still works" is very unlikley to hold true, as t will be used as a pointer to T, but it points to memory which is no longer a T. Neither solution will work when the other thread changes the type of obj while action(t) is in progress. – Stephen C. Steel Jan 26 '10 at 20:17
  • 5
    @Stephen C. Steel: You seem to be quite confused. Changing the type of `obj` would mean changing the `obj` variable itself to hold a reference to another object. It wouldn't alter the contents of memory at which resides the object originally referenced by `obj`. This original object would remain unchanged, and the `t` variable would still hold a reference to it. – P Daddy Jan 26 '10 at 21:29
  • http://www.infoq.com/news/2010/01/CDS-Dictionary – Ruben Bartelink Jan 27 '10 at 13:10
  • 1
    @P Daddy - I think you are correct, and I was wrong: if obj was rebound from a T object to a T2 object, then t would still be pointing at the old T object. Since t still references the old object, it can't be garbage collected, so the old T object will remain valid. My race condition detector circuits were trained on C++, where similar code using dynamic_cast would be a potential problem. – Stephen C. Steel Jan 27 '10 at 16:30