这段C#代码是否合法?

4

我已经将A.Test()声明为public virtual,并将B.Test()声明为private new
我从继承自BC中调用base.Test()

这段代码可以在Mono 2.10.2上编译通过,但会抛出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 ();
    }
}

这是我收到的异常:

System.MethodAccessException: 方法 TestBug.B:Test() 无法从方法 TestBug.C:.ctor() 访问

这是正确的行为吗?

Microsoft .NET或更新版本的Mono是否编译通过?
C#规范对此有何规定?
它是否随着C#版本的变化而改变?


1
如果您想在子类中访问private方法,那是不可能的。如果您希望该方法对其他类隐藏但在子类中可用,则应使用protected - Doan Cuong
你想做什么?从孙子类C访问公共A.Test方法? - statueuphemism
看起来这是您正在使用的Mono版本中的一个错误。 - Jon Skeet
@statueuphemism 这是在 .NET RX 库代码中的行为,所以我不确定他们的意思。 - Dan Abramov
@Doan 这段代码示例是从微软 Rx 库中改编的。 - Dan Abramov
1个回答

12
这是有效的C#代码,但是Mono 2.10.2编译器似乎做错了事情。使用MS编译器,对base.Test()的调用被编译为:
IL_0008:  ldarg.0
IL_0009:  call       instance void A::Test()

Mono 3.0.6.0编译器的工作方式相同。

就A而言,B.Test()实际上不存在。

事实上,C# 5规范的第3.7节甚至给出了一个非常类似于您的显式示例:

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.

我强烈怀疑Mono 2.10.2是盲目地插入了一个对B.Test()的调用——不是因为它看到了私有方法的存在,而只是为了确保“调用基类方法”。恰好在执行时,这会导致问题。关于调用哪个基类方法的选择是一个有趣的问题,因为B在C的编译时和执行时可能会改变,以覆盖Test()...此时行为是不明显的。Eric Lippert在他的博客文章中谈到了这个问题,你可能会感兴趣。

1
@Jasmine:因为B.Test()是私有的。它甚至对C都不可见;C不知道它的存在,所以它调用了它知道的方法。 - Jon Skeet
@Jon,感谢你的光临!这真的非常有用,因为我现在无法升级到Mono 3。我想在我的情况下,快速解决方法是将base.Test()更改为this.Test()C没有覆盖Test,所以无论如何都不会有影响)。 - Dan Abramov
1
@DanAbramov:这取决于 - 如果它编译为对 B.Test() 的调用,那么它仍然是有问题的 :( 不过你可以使用 A a = this; a.Test(); 代替。或者如果有选择的话,即使你在Mono上运行,也可以使用MS编译器编译 - Jon Skeet
@Jon:它没有抛出异常,所以我猜调用this.Test()最终起作用了。如果你好奇的话,这里是相关的Rx代码 - Dan Abramov
@Jon 对不起,我误读了我加粗的陈述作为与其前面的句子同一思路的一部分:“选择调用哪个基类方法是一个有趣的问题,因为B可能会在C的编译时和执行时之间发生变化,以覆盖Test()... 在这一点上,行为是不明显的。” - statueuphemism
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接