Question

I have A.Test() declared as public virtual and B.Test() declared as private new.
I'm calling base.Test() from C that inherits B.

This code compiles with Mono 2.10.2 but throws a MethodAccessException:

class A {
    public virtual void Test () { }
}

class B : A {
    private new void Test () { }
}

class C : B {
    public C ()
    {
        base.Test ();
    }

    public static void Main (string[] args)
    {
        var c = new C ();
    }
}

Here is the exception I get:

System.MethodAccessException: Method TestBug.B:Test () is inaccessible from method TestBug.C:.ctor ()

Is this the correct behavior?

Does this compile in Microsoft .NET or with newer versions of Mono?
What does C# spec say about this?
Does it vary with C# version?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top