Lor*_*tté 8 c# clr overriding cil
使用ildasm/ilasm时,您可以观察编译器生成的MSIL/CIL代码(例如C#编译器),在某些情况下,您可以看到有标记为的方法virtual final.
final在这种情况下意味着什么?我的猜测是它意味着"此方法无法被覆盖",因此即使此方法在虚拟表中有一个插槽,该插槽也不能被派生类中的方法覆盖.
但是怎么样?它在实践中如何运作?
它只是由编译器强制执行(例如,如果您尝试覆盖密封方法,它将失败并出现编译错误)或CLR(例如,当您尝试重写方法时,CLR抛出),或两者都执行?有没有合法的方法"删除",绕过这个final,即编写一个覆盖此方法的派生类?
更新:我可能已经找到一种方法(见我自己的答案),但我不知道这一切是合法的,支持的,标准的,所以,我已经张贴在这个新的(但相关的)问题,另外一个问题在这里
我愿意接受评论,当然还有其他方式(如果它们存在!)来做.
这适用于来自某个界面的方法.这是一个实现细节.如果一个类实现了某个接口,则接口的方法被标记为virtual,即多态行为(虚拟表中的位置(v表)).但它也标记为final (密封),以便实现基类的其他子类不能覆盖该特定方法.
请考虑以下示例:
interface SomeInterface
{
void SomeMethod();
}
class SomeClass : SomeInterface
{
public void SomeMethod() //This will be marked as virtual final in IL
{
//anything
}
}
Run Code Online (Sandbox Code Playgroud)
C#编译器要求将实现接口方法签名的方法标记为public.CLR要求将接口方法标记为虚拟.如果未在源代码中将该方法明确标记为虚拟,则编译器会将该方法标记为虚拟并密封; 这可以防止派生类重写接口方法.如果将方法明确标记为虚拟,则编译器将该方法标记为虚拟(并使其保持未密封状态); 这允许派生类重写接口方法.
在这种情况下最终意味着什么?我的猜测是,这意味着“此方法无法被重写”,因此即使此方法在虚拟表中有一个槽,该槽也不能被派生类中的方法覆盖。
是的。它本身相当于 C# 关键字,sealed例如:
public override sealed string ToString()
{
return "";
}
Run Code Online (Sandbox Code Playgroud)
变成:
.method public final hidebysig virtual instance string ToString () cil managed
{
ldstr ""
ret
}
Run Code Online (Sandbox Code Playgroud)
virtual与 C# 关键字相关virtual,也与任何其他虚拟方法相关。这包括override任何接口实现。特别是,虽然您无法像virtual sealed在 C# 中那样定义方法(因为这毫无意义,因此您应该决定您想要做什么),但您可以在 CIL 中定义方法,并且这是在某些接口实现中完成的。
从 C# 的角度来看,可以通过三种方式实现接口方法或属性:
从 CIL 的角度来看,所有这些都是virtual因为它们都使用虚拟方法机制。第一第三也final。
(考虑到我们可以忽略覆盖机制并call在 CIL 中使用它来调用类中定义的方法,但我们必须callvirt与接口一起使用,因为我们不知道我们正在调用哪个类,所以有必须是查找)。
它只是由编译器(例如,如果您尝试重写密封方法,它将因编译错误而失败)或由 CLR(例如,当您尝试重写方法时,CLR 抛出异常)强制执行,还是由两者强制执行?
两个都。
C# 编译器不允许您重写密封方法:
public class Test
{
public override sealed string ToString()
{
return "a";
}
}
public class Test1 : Test
{
public override string ToString()
{
return "";
}
}
Run Code Online (Sandbox Code Playgroud)
这拒绝编译,并出现编译器错误 CS0239:“'Test1.ToString()':无法覆盖继承成员 'Test.ToString()',因为它是密封的”。
但如果你自己编写 CIL 来强制执行:
.class public auto ansi beforefieldinit Test extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
}
.method public final hidebysig virtual instance string ToString () cil managed
{
ldstr "a"
ret
}
}
.class public auto ansi beforefieldinit Test2 extends Mnemosyne.Test
{
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
IL_0000: ldarg.0
IL_0001: call instance void Mnemosyne.Test::.ctor()
IL_0006: ret
}
.method public hidebysig virtual instance string ToString () cil managed
{
ldstr ""
ret
}
}
Run Code Online (Sandbox Code Playgroud)
然后,如果您调用new Test().ToString()它"a",它会按您的预期返回,但如果您调用,new Test1().ToString()则会出现运行时错误:
System.TypeLoadException : Declaration referenced in a method implementation cannot be a final method. Type: 'TestAssembly.Test2'. Assembly: 'TestAssembly, Version=1.0.0.1, Culture=neutral, PublicKeyToken=null'.
Run Code Online (Sandbox Code Playgroud)
事实上,这使得整个类成为可卸载类型;是new Test1()抛出的,而不是ToString().
是否有任何合法的方法来“删除”、规避这个最终方法,即编写一个重写此方法的派生类?
不会。TypeLoadException任何尝试重写final方法的类都会发生这种情况。您唯一的选择是编辑基类并再次编译它。
| 归档时间: |
|
| 查看次数: |
684 次 |
| 最近记录: |