Does it make sense to use "as" instead of a cast even if there is no null check? [closed]
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?
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()
IMHO, as
just make sense when combined with a null
check:
var y = x as T;
if (y != null)
y.SomeMethod();
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.
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.
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.
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.
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;
}
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;
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.
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!
I believe that the as
keyword could be thought of as a more elegant looking version of the
dynamic_cast
from C++.
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)
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
}
精彩评论