Both C++ and C# offer the cast () operator. C# also offers the "as" operator syntax which does almost the same thing and is considered by some to look prettier and be more C#'ish. But using the "as" keyword has downsides that you may not know about.
Consider the following two methods. Please skip the question of "Why would you take an object and cast to a string instead of just casting to a string?" as this is a contrived example to illustrate the differences between () and the as keyword, and in real applications there are many different reasons for casting down.
class SomeType {
bool IsTooLong1(object obj) {
string someString = obj as string;
return someString.Length > 20;
}
bool IsTooLong2(object obj) {
string someString = (string)obj;
return someString.Length > 20;
}
}
If you pass an object that is not a string, IsTooLong1 will throw a NullReferenceException whereas IsTooLong2 will throw an InvalidCastException. Obviously an InvalidCastException helps you more when you are debugging to find the problem as it tells you immediately that the wrong type of object was passed to something. The NullReferenceException could be thrown if you passed null into the method or the wrong type of object, so besides the fact that NullReferenceException doesn't immediately suggest that it could be a bad cast, it also leaves ambiguity as to the cause of the exception.
Why the difference? The "as" keyword attempts to cast the object and if the cast fails null is returned silently. The () cast operator will throw an exception immediately if the cast fails.
There are good applications to use each operator. The above shows an example of where the () operator is more appropriate. But here's an example of where the "as" keyword is better:
class SomeType {
int someField;
// The numeric suffixes on these methods are only added for reference later
public override bool Equals1(object obj) {
SomeType other = obj as SomeType;
if (other == null) return false;
return someField == other.SomeField;
}
public override bool Equals2(object obj) {
if (obj == null) return false;
// protect against an InvalidCastException
if (!(obj is SomeType)) return false;
SomeType other = (SomeType)obj;
return someField == other.SomeField;
}
}
The Equals1 method above is more efficient (and easier to read) than Equals2, although they get the same job done. While Equals1 compiles to IL that performs the type checking and cast exactly once, Equals2 compiles to do a type comparison first for the "is" operator, and then does a type comparison and cast together as part of the () operator. So using "as" in this case is actually more efficient. The fact that it is easier to read is a bonus.
In conclusion, only use the C# "as" keyword where you are expecting the cast to fail in a non-exceptional case. If you are counting on a cast to succeed and are unprepared to receive any object that would fail, you should use the () cast operator so that an appropriate and helpful exception is thrown.
8 comments: