166

Possible Duplicate:
Casting vs using the ‘as’ keyword in the CLR

I recently learned about a different way to cast. Rather than using

SomeClass someObject = (SomeClass) obj;

one can use this syntax:

SomeClass someObject = obj as SomeClass;

which seems to return null if obj isn't a SomeClass, rather than throwing a class cast exception.

I see that this can lead to a NullReferenceException if the cast failed and I try to access the someObject variable. So I'm wondering what's the rationale behind this method? Why should one use this way of casting rather than the (old) one - it only seems to move the problem of a failed cast "deeper" into the code.

Community
  • 1
  • 1
Chris
  • 9,209
  • 16
  • 58
  • 74
  • 1
    Related and very informative: http://stackoverflow.com/questions/496096/casting-vs-using-the-as-keyword-in-the-clr/496167#496167 – JYelton Feb 07 '11 at 22:14
  • 3
    For more thoughts on this question see my article on the subject: http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx – Eric Lippert Feb 08 '11 at 00:30
  • http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx – NoWar Mar 31 '14 at 16:24
  • Updated link for Eric's comment: https://web.archive.org/web/20150715143355/http://blogs.msdn.com/b/ericlippert/archive/2009/10/08/what-s-the-difference-between-as-and-cast-operators.aspx – Ryan_S Mar 11 '21 at 19:55

10 Answers10

213

With the "classic" method, if the cast fails, an InvalidCastException is thrown. With the as method, it results in null, which can be checked for, and avoid an exception being thrown.

Also, you can only use as with reference types, so if you are typecasting to a value type, you must still use the "classic" method.

Note:

The as method can only be used for types that can be assigned a null value. That use to only mean reference types, but when .NET 2.0 came out, it introduced the concept of a nullable value type. Since these types can be assigned a null value, they are valid to use with the as operator.

zwcloud
  • 4,546
  • 3
  • 40
  • 69
Brian Ball
  • 12,268
  • 3
  • 40
  • 51
  • 14
    Additionally, the "classic" cast can perform conversions. `as` can only do reference/boxing conversions. – Jeff Mercado Feb 07 '11 at 21:20
  • 4
    To be precise, you can use "as" also with `Nullable` types (and nullable types are value types...) – digEmAll Feb 07 '11 at 21:22
  • Uh, no. Nullable types are reference types, because a value type cannot be assigned null. The `Nullable` class is, quite simply, a reference wrapper that boxes the value type, that can itself be null. – KeithS Feb 07 '11 at 21:34
  • 5
    `Nullable` is a value type (declared as struct if you look at it with reflector). There must be a special rule in the runtime for nullable types, since if I try to cast using "as" to an int, the exact compiler error is "The as operator must be used with a reference type or nullable type ('int' is a non-nullable value type)" – Brian Ball Feb 07 '11 at 21:41
  • As I mention below in my answer, `as` requires that the type can be assigned the value `null`. All reference types can be assigned `null`, and if the value type supports it (as `Nullable` does) then it too can be used. – Zooba Feb 07 '11 at 21:48
  • I see what you are saying, but by definition, a value type cannot be assigned `null`, so I assuming nullable types are using what I like to call "black magic" (i.e. special logic in the runtime to support it). – Brian Ball Feb 07 '11 at 21:58
  • 1
    @Brian Ball: "there must be special rule in the runtime" — I believe it just makes use of regular implicit and explicit conversion operators, so no special rule. – Paul Ruane Feb 07 '11 at 22:59
  • 3
    @Brian, @Paul: There's no runtime "black magic". The C# and VB.NET languages have specific logic built in to handle dealing with `Nullable`. Comparisons against `null` are turned into `.HasValue`, and assignments to null are turned into `=new Nullable()` (or whatever type is appropriate, obviously). – Adam Robinson Feb 08 '11 at 14:02
  • @Adam: "specific logic built in to handle..." - that is my definition of "black magic" :). It is essentially something that can't be done by an end developer or can't be done without digging deeply into the runtime. – Brian Ball Feb 08 '11 at 15:39
  • 3
    @Brian: As I said, there's no *runtime* "black magic". There's certainly magic abounding, but it's entirely compile-time and language specific. The runtime does not treat `Nullable` any differently than it treats any other value type. – Adam Robinson Feb 08 '11 at 15:43
46

Null comparison is MUCH faster than throwing and catching exception. Exceptions have significant overhead - stack trace must be assembled etc.

Exceptions should represent an unexpected state, which often doesn't represent the situation (which is when as works better).

Matěj Zábský
  • 16,909
  • 15
  • 69
  • 114
  • Nowadays developers do not need to think what is faster at such non-significant matter. What does "stack trace must be assembled" mean? Exceptions do not always represent an unexpected state. Is FileNotFoundException always an unexpected state? – meir May 11 '12 at 12:40
  • 27
    I think `FileNotFoundException` IS an unexpected state. You tried to do something using a file without checking if it exists first, or you DID determine the file should exist and it unexpectedly did not exist. No? :) – kenchilada Oct 18 '12 at 20:41
  • 2
    The usual "premature optimization" argument applies here: (a) performance-sensitive code is a small fraction of the codebase, (b) in all other code, elegance (in terms of modelling the application domain semantics more naturally), readability and openness to testing are far weightier design objectives, because we're saving expensive developer time here, versus the nanoseconds that it takes to assemble a stack trace, etc. – Evgeni Sergeev Mar 22 '19 at 23:35
31

In some cases, it's easily to deal with a null than an exception. In particular, the coalescing operator is handy:

SomeClass someObject = (obj as SomeClass) ?? new SomeClass();

It also simplifies code where you are (not using polymorphism, and) branching based on the type of an object:

ClassA a;
ClassB b;
if ((a = obj as ClassA) != null)
{
    // use a
}
else if ((b = obj as ClassB) != null)
{
    // use b
}

As specified on the MSDN page, the as operator is equivalent to:

expression is type ? (type)expression : (type)null

which avoids the exception completely in favour of a faster type test, but also limits its use to types that support null (reference types and Nullable<T>).

Zooba
  • 11,221
  • 3
  • 37
  • 40
  • Do you really find these codes simplified? I cannot find a case where your first code is useful - "if obj is not of type SomeClass, then use a new object". The second code I would write as if (obj is ClassA) {ClassA a = (ClassA) obj; ...} - and it will definitely be more simple and easy to understand. – meir May 11 '12 at 12:45
  • @meir The first example is more realistic if you replace the first two `SomeClass`s with `ISomeFeature` that the provided object does not *need* to implement; if it doesn't, use a default implementation. For the second example, FxCop will warn about double-casting (performance) for your version, though type-testing indicates poor design anyway, so performance is a secondary concern. I try and only provide best-practice code on SO and followed MS's own guidelines in this case. – Zooba May 15 '12 at 00:28
  • 2
    if ((a = obj as ClassA) != null) Can be written if (obj is ClassA a) which is shorter and better – Nk54 Jan 21 '19 at 16:08
7

The as operator is useful in a couple of circumstances.

  1. When you only need to know an object is of a specific type but don't need to specifically act on members of that type
  2. When you'd like to avoid exceptions and instead explicitly deal with null
  3. You want to know if there is a CLR conversion between the objects and not just some user defined conversion.

The 3rd point is subtle but important. There is not a 1-1 mapping between which casts will succeed with the cast operator and those which will succeed with the as operator. The as operator is strictly limited to CLR conversions and will not consider user defined conversions (the cast operator will).

Specifically the as operator only allows for the following (from section 7.9.11 of the C# lang spec)

  • An identity (§6.1.1), implicit reference (§6.1.6), boxing (§6.1.7), explicit reference (§6.2.4), or unboxing (§6.2.5) conversion exists from the type of E to T.
  • The type of E or T is an open type.
  • E is the null literal.
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 1
    Surely `is` is what you would use for your first case? Instead of `(expr as type) != null`, should you not do `expr is type`? – Chris Morgan Nov 21 '12 at 23:33
  • @ChrisMorgan the first case is meant for when you would then pass the value accepting values of `type`. It wasn't meant to cover the case you mentioned (for which `is` is definitely more appropriate) – JaredPar Nov 29 '12 at 18:57
3

The as keyword is useful when you genuinely don't know what type the variable might be. If you have a single function that will follow different code paths depending upon the actual type of the parameter, then you have two choices:

First, using a normal cast:

if(myObj is string)
{
    string value = (string)myObj;

    ... do something
}
else if(myObj is MyClass)
{
    MyClass = (MyClass)myObj;
}

This requires that you check the type of the object using is so that you don't try to cast it to something that will fail. This is also slightly redundant, as the is-type checking is done again in the cast (so that it can throw the exception if required).

The alternative is to use as.

string myString = myObj as string;
MyClass myClass = myObj as MyClass;

if(myString != null)
{

}
else if(myClass != null)
{

}

This makes the code somewhat shorter and also eliminates the redundant type checking.

Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • You should mention the "classic" using `is` works with all types, whereas the second using `as` will only work with reference types (i.e. classes). – John Henckel Nov 03 '22 at 16:59
3

I think the best 'rule' would be to only use the 'as' keyword when it's expected that your subject won't be the object you're casting to:

var x = GiveMeSomething();

var subject = x as String;

if(subject != null)
{
  // do what you want with a string
}
else
{
  // do what you want with NOT a string
}

However, when your subject SHOULD be of the type you're casting to, use a 'classic cast', as you call it. Because if it isn't the type you're expecting, you'll get an exception which fits the exceptional situation.

Erik van Brakel
  • 23,220
  • 2
  • 52
  • 66
1

using as will return null if not a valid cast which allows you to do other things besides wrapping the cast in a try/catch. I hate classic cast. I always use as cast if i'm not sure. Plus, exceptions are expensive. Null checks are not.

Dustin Davis
  • 14,482
  • 13
  • 63
  • 119
0

There's nothing deep happening here.. Basically, it's handy to test something to see if it's of a certain type (i.e. use 'as'). You would want to check the result of the 'as' call to see if the result is null.

When you expect a cast to work and you want the exception to be thrown, use the 'classic' method.

RQDQ
  • 15,461
  • 2
  • 32
  • 59
0

You use the "as" statement to avoid the possibility of an exception, e.g. you can handle the cast failure gracefully via logic. Only use the cast when you are sure that the object is of the desired type. I almost always use the "as" and then check for null.

Keith Bluestone
  • 166
  • 2
  • 9
0

I suppose it is useful if the result of the cast will be passed to a method that you know will handle null references without throwing and ArgumentNullException or suchlike.

I tend to find very little use for as, since:

obj as T

Is slower than:

if (obj is T)
    ...(T)obj...

The use of as is very much an edge-case scenario for me, so I can't think of any general rules for when I would use it over just casting and handling the (more informative) casting exception further up the stack.

Quick Joe Smith
  • 8,074
  • 3
  • 29
  • 33