tags:

views:

166

answers:

8

Possible Duplicate:
Casting: (NewType) vs. Object as NewType

In C#, why ever cast reference types when you can use "as"?

Casting can generate exceptions whereas "as" will evaulate to null if the casting fails.

Wouldn't "as" be easier to use with reference types in all cases?

eg:

MyObject as DataGridView

rather than,

(DataGridView)MyObject
+2  A: 

operator 'as' work with reference types only.

Arseny
It also 'fails' silently.
Noldorin
The question has been updated to clarify that it is about reference types only (as it talks about `null` I thought that was pretty clear already).
Daniel Earwicker
+2  A: 

Sometimes, you want the exception to be thrown. Sometimes, you want to try to convert and nulls are OK. As already stated, as will not work with value types.

Jarrett Meyer
Damn, y'beat me to it ;) exactly, sometimes you want an exception to be thrown, which is perfectly valid. +1
Kezzer
Why would you want the exception to be thrown?
Craig Johnston
Because the calling code has done something that doesn't make sense and the only sensible response is to throw an exception.It's no different to the way that if you called Load() on an XMLDocument with content that wasn't XML then you want it to throw an exception. Not doing so just leaves you with an object that cannot be sensibly used, but which you don't know cannot be sensibly used.
Jon Hanna
A: 

From MSDN (as (C# reference)):

the as operator only performs reference conversions and boxing conversions. The as operator cannot perform other conversions, such as user-defined conversions, which should instead be performed using cast expressions.

Oded
A: 

Taking into consideration all of the comments, we came across this just the other day and wondered why you would do a direct cast over using the keyword as. What if you want the cast to fail? This is sometimes the desirable effect you want from a cast if you're casting from a null object. You then push the exception up the call stack.

So, if you want something to fail, use a direct cast, if you're okay with it not failing, use the as keyword.

Kezzer
Why would you want the cast to fail? Besides, if the cast fails using "as" you will be given null.
Craig Johnston
Some software environments cater for generic types, like ours does, in order to increase the flexibility. New types can be introduced ad-hoc. For example, we use dynamic casters that can convert from one type to another even though they're not related. This is done so the end-type is "user-friendly" (perhaps immutable, or with hidden fields). If the cast doesn't work, we need to know about it!
Kezzer
+1  A: 

If, when you write the code to make the cast, you are sure that the cast should work, you should use (DataGridView)MyObject. This way, if the cast fails in the future, your assumption about the type of MyObject will cause an invalid cast exception at the point where you make the cast, instead of a null reference exception at some point later.

If you do want to handle the case where MyObject is not a DataGridView, then use as, and presumably check for it being null before doing anything with it.

tl;dr If your code assumes something, and that assumption is wrong at run-time, the code should throw an exception.

Douglas
+8  A: 

Consider the following alternatives:

Foo(someObj as SomeClass);

and:

Foo((SomeClass)someObj);

Due to someObj being of the wrong type, the first version passes null to Foo. Some time later, this results in a NullReferenceException being thrown. How much later? Depends on what Foo does. It might store the null in a field, and then minutes later it's accessed by some code that expects it to be non-null.

But with the second version, you find the problem immediately.

Why make it harder to fix bugs?

Update

The OP asked in a comment: isn't is easier to use as and then check for null in an if statement?

If the null is unexpected and is evidence of a bug in the caller, you could say:

SomeClass c = someObj as SomeClass;
if (c == null)
{
    // hmm...
}

What do you do in that if-block? There are two general solutions. One is to throw an exception, so it is the caller's responsibility to deal with their mistake. In which case it is definitely simpler to write:

SomeClass c = (SomeClass)someObj;

It simply saves you writing the if/throw logic by hand.

There is another alternative though. If you have a "stock" implementation of SomeClass that you are happy to use where nothing better is available (maybe it has methods that do nothing, or return "empty" values, etc.) then you could do this:

SomeClass c = (someObj as SomeClass) ?? _stockImpl;

This will ensure that c is never null. But is that really better? What if the caller has a bug; don't you want to help find bugs? By swapping in a default object, you disguise the bug. That sounds like an attractive idea until you waste a week of your life trying to track down a bug.

(In a way this mimics the behaviour of Objective-C, in which any attempt to use a null reference will never throw; it just silently does nothing.)

Daniel Earwicker
+1 I keep seeing `Foo(someObj as SomeClass);` in code (without a `null` check, that is). It baffles me as to why people do this. Is there a book somewhere that says `(SomeClass)` are bad?
Tim Robinson
Isn't it easier to check whether the variable is null after the "as" statement, rather than dealing with the an exception?
Craig Johnston
Can you do anything useful when `someObj` is `null`? If not -- if your code can assume that `someObj` is in fact `SomeClass` -- then what's the point?
Tim Robinson
But what if you didn't write `Foo`, and I did? How do I know, upon recieving a null program argument if you meant it to be null or not? Maybe my code is meant to be able to accept null arguments, but you thought you were passing an argument that was non-null and of an okay class. If you used a cast, you'd be told immediately. If you used an `as`, you don't get to know until you try and do something later and get a result you don't expect (which might not even be an exception!)
Stephen
@Tim Robinson - if the reference is allowed to be null, then yes, you would of course want to use `as`. Do I need to update the answer to clarify that?
Daniel Earwicker
Useful rule for nulls: treat them as poisonous. Reject them in your inputs. Avoid returning them as outputs.
Tim Robinson
@Daniel - that comment was for @Craig
Tim Robinson
Ah I see, sorry.
Daniel Earwicker
@Tim: folks coming from C++ learn that `()` casts are bad, and transfer this to C# despite the very different cast semantics.
Anton Tykhyy
@Tim, sometimes it's reasonable. Common example is if you've implemented `IEquatable<T>.Equals()` and it includes the null-check it should include, then the appropriate override for `Object.Equals()` is almost always `return Equals(obj as MyType)`, as the result of passing null and a different type *should* be the same in this case.
Jon Hanna
@Jon Hanna - that's an example of a situation where the target reference is allowed to be `null`, as the "contract" of `Object.Equals` is supposed to handle it by design. The point here is not to deny the existence of those situations, but that they are comparatively rare, and so it is usually better to stop `null`s from finding their way into data structures as soon as possible.
Daniel Earwicker
Really the underlying problem here is that the language has no support for non-nullable references, despite the fact that the overwhelming majority of references in real programs never need to store the value `null`. http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake
Daniel Earwicker
+1  A: 

One definite reason is that the object is, or could be (when writing a generic method, you may not know at coding-time) being cast to a value type, in which case as isn't allowed.

One more dubious reason is that you already know that the object is of the type in question. Just how dubious depends on how you already know that. In the following case:

if(obj is MyType)
  DoMyTypeStuff((MyType)obj);
else
  DoMoreGeneralStuff(obj);

It's hard to justify using as here, as the only thing it really does is add a redundant check (maybe it'll be optimised away, maybe it won't). At the other extreme, if you are half-way to a trance state with the amount of information you've got in you're brain's paged-in memory and on the basis of that you are pretty sure that the object must be of the type in question, maybe it's better to add in the check.

Another good reason is that the difference between being of the wrong type and being null gets hidden by as. If it's reasonable to be passing in a string to a given method, including a null string, but it's not reasonable to pass in an int, then val as string has just made the incorrect usage look like a completely different correct usage, and you've just made the bug harder to find and potentially more damaging.

Finally, maybe if you don't know the type of the object, the calling code should. If the calling code has called yours incorrectly, they should receive an exception. To either allow the InvalidCastException to pass back, or to catch it and throw an InvalidArgument exception or similar is a reasonable and clear means of doing so.

Jon Hanna
`MyType t = obj as MyType; if (t != null) { DoMyTypeStuff(t); } else { DoMoreGeneralStuff(obj); }`
Tim Robinson
A: 

As is faster and doesn't throw exceptions. Therefore it is generally preferred. Reasons to use casts include:

Using as, you can only assign types that are lower in the inheritance tree to ones that are higher. For example:

object o = "abc" as object;
DataGridView d = "abc" as DataGridView // doesn't do anything

DataGridView could create a custom cast that does allow this. Casts are defined on the target type and therefore allow everything, as long as it's defined.

Another problem with as is that it doesn't always work. Consider this method:

IEnumerable<T> GetList<T>(T item)
{
  (from ... select) as IEnumerable<T>
}

This code fails because T could also be a Value Type. You can't use as on those because they can never be null. This means you'll have to put a constraint on T, while it is actually unnecesary. If you don't know whether you're going to have a reference type or not, you can never use as.

Of course, you should always check for null when you use the as keyword. Don't assume no exceptions will be thrown just becase the keyword doesn't throw any. Don't put a Try {} Catch(NullReferenceException){} around it, that't unneccesary and bloat. Just assign the value to a variable and check for null before you use it. Never use it inline in a method call.

Jouke van der Maas