Yet Another Comment on the C# “Cast” ( ) and “as” Operators…
Time and again, the question comes up whether to prefer the cast ( )
or the as
operator in C# to perform conversions to reference types. According to the C# language specification, the two operators behave differently in several respects:
C# Conversion Operator | ( ) | as |
---|---|---|
Name |
Cast operator |
as operator |
C# 3.0 Specification section | 7.6.6 | 7.9.11 |
Can convert to ValueType
|
Yes | No |
Executes user-defined explicit conversions | Yes | No |
Compile-time errors | CS0030: Cannot convert type 'X' to 'Y'. |
|
Runtime behavior if not convertible | Throws InvalidCastException : Unable to cast object of type 'X' to type 'Y'.
|
Returns null
|
Given the above comparison table, it follows that you must use the cast operator ( )
in the following cases:
- to convert to
ValueType
:object o = 137; var i = (int)o;
- to convert via a user-defined explicit operator overload defined in the source type. Note that the overload may return anything, depending on its implementation: The same instance, a new instance of the same type, a new instance of a completely non-related type,
null
, aValueType
or reference type...// Declared inside a class called Source: static explicit operator Target(Source s) {return new Target();}
By contrast, you must use the as
operator in the following cases:
- to be 100% sure that that the result points either to the same instance, or
null
:var bar = foo as Bar;
- to return
null
if a runtime conversion to the desired reference type is not possible, as in this construct:var bar = foo as Bar ?? new Bar();
Furthermore, the as
operator is a bit more tolerant with generics at compile time than the cast operator. That's because the as
operator casts directly to a type of the same inheritance line:
TDerived GetDerived<TBase, TDerived>(TBase b) where TDerived: class {
// Produces compiler error CS0030: Cannot convert
// type 'TBase' to 'TDerived'.
// return (TDerived)b;
// This compiles...
return b as TDerived;
}
In all other cases, from a technical point of view, the choice between cast and as
is irrelevant. Therefore, the right decision depends on what you want to express most in the source code. As a matter of taste, I find the as
operator easier to read, as it goes from left to right, which is the natural way of reading source code, whereas the cast ( )
operator forces the mind to jump back and forth. It also looks like old-fashioned C code.
Some tools, such as JetBrains' ReSharper, produce a warning if one applies the as
operator without checking for null
within the same scope. Unfortunately, this kind of flow analysis does not work when the null
-checking logic is defined in a separate external helper method. The warning can be turned off, but only on a per-user basis. It may also be turned off with a specific ReSharper comment surrounding the critical section, but this leads to ugly cluttered code. Thus, if your company uses ReSharper, it might be a pragmatic reason to prefer the cast ( )
operator over the as
operator.
In my opinion, it would be better if the C# as
operator did not return null
, but throw an InvalidCastException
on invalid casts. Nothing of importance would be lost—you can always check for null
with the is
operator—but a lot of misunderstandings would be prevented. Interestingly, the VB.NET language has the DirectCast
operator, which does exactly this. A similar behavior could be implemented in C# like this:
public static T As<T>(this object o) where T: class{
if (o == null) throw new NullReferenceException();
if (!(o is T)) throw new InvalidCastException();
return o as T;
}
Last not least, in F#, explicit or implicit conversion operators do not even exist. There is only the as operator...