In general, you should avoid casting wherever possible. Casting often indicates poor class design, but there are times when it is required. It is relatively common to need to convert between unsigned and signed integers between various third-party APIs, for example. Casting objects should be much rarer.
Casting objects is never free, but the costs differ dramatically depending on the relationship of the objects. Casting an object to its parent is relatively cheap. Casting a parent object to the correct child is significantly more expensive, and the costs increase with a larger hierarchy. Casting to an interface is more expensive than casting to a concrete type.
What you absolutely must avoid is an invalid cast. This will cause an InvalidCastException exception to be thrown, which will dwarf the cost of the actual cast by many orders of magnitude.
See The CastingPerf sample project in the accompanying source code which benchmarks a number of different types of casts. It produces this output on my computer in one test run:
JIT (ignore): 1.00x No cast: 1.00x Up cast (1 gen): 1.00x Up cast (2 gens): 1.00x Up cast (3 gens): 1.00x Down cast (1 gen): 1.25x Down cast (2 gens): 1.37x Down cast (3 gens): 1.37x Interface: 2.73x Invalid Cast: 14934.51x as (success): 1.01x as (failure): 2.60x is (success): 2.00x is (failure): 1.98x
The ‘is’ operator is a cast that tests the result and returns a Boolean value. The ‘as’ operator is similar to a standard cast, but returns null if the cast fails. From the results above, you can see this is much faster than throwing an exception.
Never have this pattern, which performs two casts:
if (a is Foo) { Foo f = (Foo)a; }
Instead, use ‘as’ to cast and cache the result, then test the return value:
Foo f = a as Foo; if (f != null) { ... }
If you have to test against multiple types, then put the most common type first.
Note One annoying cast that I see regularly is when using MemoryStream.Length, which is a long. Most APIs that use it are using the reference to the underlying buffer (retrieved from the MemoryStream.GetBuffer method), an offset, and a length, which is often an int, thus making a downcast from long necessary. Casts like these can be common and unavoidable.
For all your application development needs, visit www.verbat.com for a fiscally conscious proposal that meets your needs ( So I can keep this blog going as well!!!!)
Alternatively click through the link if you found this article interesting. (This will help the companies Search engine rankings)
Leave a Reply