It's valid C#, but the Mono 2.10.2 compiler is apparently doing the wrong thing. With the MS compiler, the call to base.Test()
is compiled to:
IL_0008: ldarg.0
IL_0009: call instance void A::Test()
The Mono 3.0.6.0 compiler works the same way.
As far as A
is concerned, B.Test()
effectively doesn't exist.
In fact, section 3.7 of the C# 5 spec even gives an explicit example which is very similar to yours:
A declaration of a new member hides an inherited member only within the scope of the new member.
class Base { public static void F() {} } class Derived: Base { new private static void F() {} // Hides Base.F in Derived only } class MoreDerived: Derived { static void G() { F(); } // Invokes Base.F }
In the example above, the declaration of F in Derived hides the F that was inherited from Base, but since the new F in Derived has private access, its scope does not extend to MoreDerived. Thus, the call F() in MoreDerived.G is valid and will invoke Base.F.
I strongly suspect that the Mono 2.10.2 is blindly inserting a call to B.Test()
- not because it sees the private method's existence, but just to ensure that "a base class method is called". As it happens, that goes badly at execution time. The choice about which base class method to call is an interesting one, as B
could change between C's compile-time and execution time, to override Test()
... at which point the behaviour is non-obvious. Eric Lippert talks about this in a blog post which you may find interesting.