用不同的名称覆盖 final (IL) / sealed (C#) 方法是否合法?
Is overriding a final (IL) / sealed (C#) method with a different name legal?
我有一个层次结构 classes:
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();
}
不过好像有办法。在 IL 中,您可以重写具有不同名称的方法。所以,我们改个名字(M1_2()
overrides M1()
),说是重写base上的方法class(C1::M1()
),a la显式接口实现,和中间的"final"(C2
)class无所谓了。
.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
ILasm 会很乐意 assemble 它,它在 ILSpy 中显示为
public class C3 : C2
{
object C1.M1_2()
然后在同一个class中,你可以定义一个new M1
调用this.M1_2()
。
所以你有 1) 重写 M1
(使用不同的名称,但仍然......)和 2)在 C3 中有一个 M1
方法(它是一个 "bridge",但它是你见)。
但看起来……不对。还是合法的?
如果你打电话
C1 obj = new C3();
obj.M1();
然后 M1_2
被正确调用(我在调试器中验证了它)。似乎 CLR 仅在链是直接的 (C1::M1 > C2::M1 > C3::M1
) 时才强制执行 final
约束,如果您在层次结构 (C1::M1 > C3::M1_2
) 上执行 "jump" 则不会。不过,您必须选择一个不同的名称。如果您使用相同的名称 (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
不行,扔一个System.TypeLoadException
Additional information: Declaration referenced in a method implementation cannot be a final method
Which is totally expected.
我想知道:那些是 CLR 规则,还是我只是在实现中发现了一个极端情况? (规则中的极端情况会很好,在实施中..你不能指望它;))
看起来像是规范中的边缘案例。
在 ECMA-335 中,分区 II 第 22.27 节 MethodImpl:
MethodDeclaration shall index a method in the ancestor chain of Class (reached via its Extends chain) or in the interface tree of Class (reached via its InterfaceImpl entries) [ERROR]
The method indexed by MethodDeclaration shall not be final (its Flags.Final shall be 0) [ERROR]
因此,您尝试重写的特定方法不得密封,必须在祖先 class 上定义,但不要求指定的特定方法是您的插槽中最具体的重写祖先链.
话虽如此,未来的版本可能会对执行此类操作施加安全限制,这可能“完全出乎意料”。
我有一个层次结构 classes:
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();
}
不过好像有办法。在 IL 中,您可以重写具有不同名称的方法。所以,我们改个名字(M1_2()
overrides M1()
),说是重写base上的方法class(C1::M1()
),a la显式接口实现,和中间的"final"(C2
)class无所谓了。
.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
ILasm 会很乐意 assemble 它,它在 ILSpy 中显示为
public class C3 : C2
{
object C1.M1_2()
然后在同一个class中,你可以定义一个new M1
调用this.M1_2()
。
所以你有 1) 重写 M1
(使用不同的名称,但仍然......)和 2)在 C3 中有一个 M1
方法(它是一个 "bridge",但它是你见)。
但看起来……不对。还是合法的?
如果你打电话
C1 obj = new C3();
obj.M1();
然后 M1_2
被正确调用(我在调试器中验证了它)。似乎 CLR 仅在链是直接的 (C1::M1 > C2::M1 > C3::M1
) 时才强制执行 final
约束,如果您在层次结构 (C1::M1 > C3::M1_2
) 上执行 "jump" 则不会。不过,您必须选择一个不同的名称。如果您使用相同的名称 (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
不行,扔一个System.TypeLoadException
Additional information: Declaration referenced in a method implementation cannot be a final method Which is totally expected.
我想知道:那些是 CLR 规则,还是我只是在实现中发现了一个极端情况? (规则中的极端情况会很好,在实施中..你不能指望它;))
看起来像是规范中的边缘案例。
在 ECMA-335 中,分区 II 第 22.27 节 MethodImpl:
MethodDeclaration shall index a method in the ancestor chain of Class (reached via its Extends chain) or in the interface tree of Class (reached via its InterfaceImpl entries) [ERROR]
The method indexed by MethodDeclaration shall not be final (its Flags.Final shall be 0) [ERROR]
因此,您尝试重写的特定方法不得密封,必须在祖先 class 上定义,但不要求指定的特定方法是您的插槽中最具体的重写祖先链.
话虽如此,未来的版本可能会对执行此类操作施加安全限制,这可能“完全出乎意料”。