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

为什么编译器为显式实现的接口方法的调用生成callvirt指令,并在以下代码中调用对implicilty实现的接口方法的调用?

编译器是mono的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用于“可空接收器”,因为它在向该方法发出跳转之前进行空检查.看来这可能是空的. (Call and Callvirt)
如果编译器可以证明接收器是非空的,则使用>调用.
>关闭优化可能会产生更多的callvirts来帮助调试器. (因此我编译了优化功能.)

在这种情况下,在我看来,这总是非null,否则我们无论如何都不会在封闭方法中结束.

单声道在这里错过了优化吗?
或者是否有可能变为空?

如果以某种方式涉及终结者,我可以想象这种情况,但这不是这里的情况.如果这可能在这里变为空,那么使用呼叫根本不是错的吗?

编辑

从@ jonathon-chase的回答和对问题的评论我现在提炼出一个工作原理:接口上的方法必须是虚拟的,因为一般来说,你不能静态地确定实现类型是否提供“普通”或虚拟/抽象实现.要确保实现类型层次结构上的虚拟方法在通过接口调用callvirt时工作是可行的方法. (请参阅我对通过接口调用隐式方法的问题的评论).

关于潜在的优化:

在我的例子中,我有一个密封类型,我只在我自己的继承层次结构中调用.编译器可以静态地确定1)实现是非虚拟的,2)在this引用上调用它,3)由于sealed关键字,层次结构是有界的;所以虚拟实现无法存在.我认为在这种情况下可以使用调用,但我也看到,与此分析所需的工作量相比,这些好处是可以忽略的.

最佳答案
看起来接口方法实现为虚拟,因此显式实现是覆盖虚拟方法实现.我越是想到这一点,显然实现真的是虚拟过载似乎越有意义.

我还没有使用单声道编译器进行检查,但是在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

转载注明原文:c# – 为什么使用callvirt调用显式实现的接口方法并且隐式实现不? - 代码日志