是否覆盖了具有不同名称的最终(IL)/密封(C#)方法?

Lor*_*tté 2 c# clr overriding cil

我有一个类的层次结构:

class C1 { virtual object M1(); }

class C2: C1 { override sealed object M1(); }

class C3: C2 { 
   // I want to override M1()
   // CSC gives me an error, obviously
   override object M1();
}
Run Code Online (Sandbox Code Playgroud)

但似乎有一种方法.在IL中,您可以使用其他名称覆盖方法.所以,我们更改名称(M1_2()覆盖M1()),说它覆盖了基类(C1::M1())上的方法,一个显式接口实现,而中间(C2)类上的"final" 不再重要.

.class public auto ansi beforefieldinit N.C3
 extends N.C2
{ 
   .method private hidebysig virtual final 
      instance object  M1_2() cil managed
   {
      .override N.C1::M1
Run Code Online (Sandbox Code Playgroud)

ILasm很乐意组装它,它在ILSpy中显示为

public class C3 : C2
{
    object C1.M1_2()
Run Code Online (Sandbox Code Playgroud)

然后在同一个类中,您可以定义new M1哪些调用this.M1_2().所以你有1)被覆盖M1(使用不同的名称,但仍然......)和2)M1在C3中有一个方法(它是一个"桥",但它是你所看到的).

但它看起来......错了.还是合法的?

如果你打电话

C1 obj = new C3();
obj.M1();
Run Code Online (Sandbox Code Playgroud)

然后M1_2被正确调用(我在调试器中验证了它).似乎CLR final仅在链是direct(C1::M1 > C2::M1 > C3::M1)时强制执行约束,而不是在层次结构(C1::M1 > C3::M1_2)上"跳转"时强制执行约束.但是,您必须选择其他名称.如果您使用相同的名称(M1):

.class public auto ansi beforefieldinit N.C3
   extends N.C2
{ 
   .method private hidebysig virtual final 
      instance object  M1() cil managed
   {
      .override N.C1::M1
Run Code Online (Sandbox Code Playgroud)

不会工作,抛出一个 System.TypeLoadException

附加信息:方法实现中引用的声明不能是完全预期的最终方法.

我想知道:那些CLR规则,还是我刚刚在实现中发现了一个极端情况?(规则中的一个角落案例就可以了,在实施中......你不能指望它;))

Bri*_*hle 5

看起来像规范中的边缘情况.

在ECMA-335中,Partition II第22.27节MethodImpl:

  1. MethodDeclaration将索引类的祖先链中的方法(通过其Extends链到达)或类的接口树(通过其InterfaceImpl条目到达)[ERROR]

  2. MethodDeclaration索引的方法不是final(Flags.Final应为0)[ERROR]

因此,您尝试覆盖的特定方法不得密封,并且必须在祖先类上定义,但不要求指定的特定方法是祖先链中该槽的最特定覆盖.

话虽如此,这可能是"足够意外",以至于未来版本可能会施加安全限制来执行此类操作.