为什么编译器为显式实现的接口方法调用生成callvirt
指令,为调用 implicilty call >在以下代码中实现了接口方法?
编译器是单声道的mcs
4.2.2,已启用优化。
public interface ITest
{
void ExplicitInterfaceMethod();
void ImplicitInterfaceMethod();
}
public sealed class Test : ITest
{
void ITest.ExplicitInterfaceMethod()
{ }
public void ImplicitInterfaceMethod()
{ }
public void InstanceMethod()
{ }
public void CallTest()
{
((ITest) this).ExplicitInterfaceMethod();
// IL_0000: ldarg.0
// IL_0001: callvirt instance void class ITest::ExplicitInterfaceMethod()
this.ImplicitInterfaceMethod();
// IL_0006: ldarg.0
// IL_0007: call instance void class Test::ImplicitInterfaceMethod()
InstanceMethod();
// IL_000c: ldarg.0
// IL_000d: call instance void class Test::InstanceMethod()
}
}
到目前为止我发现了什么:
callvirt
用于"可空接收器"因为它在向方法发出跳转之前进行空检查。似乎this
可能为空。 (Call and Callvirt)call
。callvirt
来帮助调试器。 (因此我在开启优化的情况下编译。)在这种情况下,在我看来this
始终是非空的,否则无论如何我们都不会在封闭方法中结束。
mono会在这里错过优化吗?
或者this
是否有可能成为null
?
如果终结者以某种方式参与,我可以想象这种情况,但这不是这里的情况。 如果 this
可能会在null
成为call
,那么根本不会使用callvirt
吗?
修改
从@ jonathon-chase的回答和对问题的评论我现在提炼出一个工作原理:接口上的方法必须是虚拟的,因为你通常不能静态地确定是否实施类型提供了一个普通的'或虚拟/抽象实现。通过接口sealed
调用时,确保实现类型层次结构上的虚拟方法有效。 (请参阅我对通过接口调用隐式方法的问题的评论。)
关于潜在的优化:
在我的示例中,我有一个this
类型,我只在我自己的继承层次结构中调用。编译器可以静态地确定1)实现是非虚拟的,2)它是在sealed
引用上调用的,3)层次结构是有限的,因为{{1} }关键字;所以虚拟实现无法存在。我认为在这种情况下可以使用call
,但我也发现,与此分析所需的工作量相比,这些好处是可以忽略的。
答案 0 :(得分:3)
看起来接口方法是作为虚拟实现的,因此显式实现是覆盖虚方法实现。我越是想到这一点,显然实现真的是虚拟过载似乎越有意义。
我还没有使用单声道编译器进行检查,但是在csc中使用/ target:library / optimize +后,这里是ildasm.exe的转储。如您所见,接口方法是在接口上声明的虚拟接口。将类型转换为接口时,似乎有意义的是我们为该方法提供虚拟重载,而不是在同一个类上隐式声明的方法。仍然会爱一个比我更有知识的人来衡量。
使用的代码:
using System;
public interface ITest
{
void TestMethod();
}
public class Test : ITest
{
void ITest.TestMethod()
{
Console.WriteLine("I am Test");
}
void TestMethod()
{
Console.WriteLine("I am other test");
}
}
IL输出:
.class interface public abstract auto ansi ITest
{
.method public hidebysig newslot abstract virtual
instance void TestMethod() cil managed
{
} // end of method ITest::TestMethod
} // end of class ITest
.class public auto ansi beforefieldinit Test
extends [mscorlib]System.Object
implements ITest
{
.method private hidebysig newslot virtual final
instance void ITest.TestMethod() cil managed
{
.override ITest::TestMethod
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "I am Test"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Test::ITest.TestMethod
.method private hidebysig instance void
TestMethod() cil managed
{
// Code size 11 (0xb)
.maxstack 8
IL_0000: ldstr "I am other test"
IL_0005: call void [mscorlib]System.Console::WriteLine(string)
IL_000a: ret
} // end of method Test::TestMethod