A better Safe Cast using Generics

Update: the code has changed a bit in the C# version to reflect *exactly* the same behavior as "as". See the comments.
 
Here's a nice way to make something like the "as" word in C# apply to both value types and reference types. It takes advantage of Generics in .NET 2.0 and the "default" word in C#, which returns the default value for a given type. The equivalent to that word in VB.NET as far as I've found is "Nothing". 
 
With this function you can cast to any generic type you'd like, and if the value does not cast to it, you don't get an exception, but the default value for that type of object.
Important note: for value type, this will cause a boxing operation to occur and a little perf hit because of two type checks.
 
 
8:         private T safeCastTo<T>(object obj)
9:         {
10:             if (obj == null)
11:             {
                  //same behavior as "as"
12:                 return null;
                 //or you could return the default value if you need to
12:                 //return default(T);
13:             }
14:             if (!(obj is T))
15:             {
                  //same behavior as "as"
6:                 return null;
                 //or you could return the default value if you need to
12:                 //return default(T);

17:             }
18:             return (T)obj;
19:         }
Or in VB.NET:

9:
    Function SafeCast(Of T)(ByVal value As Object) As T
10:         If value Is Nothing Then Return Nothing
11:         If TypeOf (value) Is T Then Return value
12:         Return Nothing
13:     End Function
have fun!
Published Wednesday, November 09, 2005 12:38 PM by RoyOsherove
Filed under:

Comments

Wednesday, November 09, 2005 11:10 AM by Julian M Bucknall

# re: A better Safe Cast using Generics

Roy

The boxing issue is fairly trivial. Much worse is the double type-checking. Type-checking is heavy-duty in .NET. The "is" operator will invoke type-checking, and then the cast will then invoke the same type-checking.

I think I'd rather write the code this method replaces.

Cheers, Julian

Wednesday, November 09, 2005 2:44 PM by Ron Buckton

# re: A better Safe Cast using Generics

I agree with Julian, doing an _is_ and then an _as_ or a _castclass_ is more expensive in the msil and tools like code analysis (FxCop) may yell.

The _as_ keyword is always going to be the better choise in C# (and TryCast in VB.NET) for reference types.

If you want to test for a value type with as in 2.0 use ?. e.g.

void Foo(object o)
{
int? a = o as int?;
if(a != null)
{
DoSomething((int)a); // or DoSomething(a.Value);
}
}

(With the RC and RTM releases, _as_ works with Nullable<>, as well as the coalesce operator ?? works with reference types)
Wednesday, November 09, 2005 5:29 PM by Roy Osherove

# re: A better Safe Cast using Generics

Rob, Julian, Thanks for mentioning this.
I agree that the native syntax for nullability chekcing will outperform this code.
Think of this code as an exercise in Generics. In fact, I'd probably use this code and optimize for efficieny at a later time, when it's needed. That wasy I can use the same syntax for both value types and reference types.
(Unless i'm at a place where I *know* this will be a problem sucjh as tight loops that need to run fast on a lot of objects)
Wednesday, November 09, 2005 10:41 PM by Ron Buckton

# re: A better Safe Cast using Generics

Roy,

Its not just about performance though. The _as_ keword is used to perform a cast without an exception, returning null if the cast was not possible. Since a ValueType has no concept of null _as_ would not work by default. The above method would return default(T) rather then null, giving you no way to assure that the value supplied to the _as_ actually *could* be safely cast to the supplied type.

Through the use of Nullable<> however, you then gain the ability to perform the null test, and the compiler for C# has been designed to handle using a nullable ValueType with _as_. That way you can use the same syntax as a ref type and still have the null test.

This method then becomes the most consistent as well as does not incur a cost to box the value.
Thursday, November 10, 2005 1:15 AM by Roy Osherove

# re: A better Safe Cast using Generics

Ron, yes. There is a slight difference in behavior there, isn't there? :)
A simple change such as returning null if the cast won't work instead of default would change this into the same behavior as "as".
I'll update teh post to reflect this fact.
Sunday, November 13, 2005 3:57 AM by Ernst Kuschke

# re: A better Safe Cast using Generics

It would be cool to do something to a similar effect in .NET 3.0 as an extension method to certain types ;o)