Downcasting is unpopular, maybe a code smell
I disagree. Downcasting is extremely popular; a huge number of real-world programs contain one or more downcasts. And it is not maybe a code smell. It is definitely a code smell. That's why the downcasting operation is required to be manifest in the text of the program. It's so that you can more easily notice the smell and spend code review attention on it.
in what circumstance[s] is it appropriate to write code which downcasts?
In any circumstance where:
- you have a 100% correct knowledge of a fact about the runtime type of an expression that is more specific than the compile-time type of the expression, and
- you need to take advantage of that fact in order to use a capability of the object not available on the compile-time type, and
- it is a better use of time and effort to write the cast than it is to refactor the program to eliminate either of the first two points.
If you can cheaply refactor a program so that either the runtime type can be deduced by the compiler, or to refactor the program so that you don't need the capability of the more derived type, then do so. Downcasts were added to the language for those circumstances where it is hard and expensive to thus refactor the program.
why is downcasting supported by the language?
C# was invented by pragmatic programmers who have jobs to do, for pragmatic programmers who have jobs to do. The C# designers are not OO purists. And the C# type system is not perfect; by design it underestimates the restrictions that can be placed on the runtime type of a variable.
Also, downcasting is very safe in C#. We have a strong guarantee that the downcast will be verified at runtime and if it cannot be verified then the program does the right thing and crashes. This is wonderful; it means that if your 100% correct understanding of type semantics turns out to be 99.99% correct, then your program works 99.99% of the time and crashes the rest of the time, instead of behaving unpredictably and corrupting user data 0.01% of the time.
EXERCISE: There is at least one way to produce a downcast in C# without using an explicit cast operator. Can you think of any such scenarios? Since these are also potential code smells, what design factors do you think went into the design of a feature that could produce a downcast crash without having a manifest cast in the code?