为什么使用callvirt调用显式实现的接口方法并且隐式实现了?

时间:2016-01-23 23:12:15

标签: c#

为什么编译器为显式实现的接口方法调用生成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,但我也发现,与此分析所需的工作量相比,这些好处是可以忽略的。

1 个答案:

答案 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