The statement was: "That also means you can't call the original version
without casting to the base class.". This statement is false.
Of course the only proper solution is to declare f() virtual in Foo.
But that may be good enough if you have control over all the code
that's using the class. That is, you can create a Bar object, and its
f() method will do what you want as long as you never assign it to a
Foo variable, never pass it to a method that expects a Foo, and none of
the inherited methods from Foo call f().
It's *very* hard to be sure you have control over all the code using the
class, for one thing, the type-system won't help you at all, neither
staticly or dynamicly. Since you inherit from Foo, and need to override
f() in Foo it's rather likely that some other code is going to use your
Bar instance as a Foo.
Using "new" to override a method that should have been virtual is a hack
that leads to unreadable code. If you do have complete control over the
code that uses Bar it can be replaced by, either naming the f() in Bar
something else -- proper to it's usage in Bar, or:
class Bar {
public void HACK_SINCE_f_IS_NOT_VIRTUAL() { /* impl. of f() */ }
public new f() {
throw new NotSupported(
string.format("call HACK_SINCE_f_IS_NOT_VIRTUAL instead"));
}
Which clearly shows that a hack is in progress.