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, aValueTypeor 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
nullif 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...