为代表关键字编译器生成密封的类包含虚拟方法(Compiler generated sealed cl

2019-06-26 03:52发布

delegate关键字将在C#所使用的,C#编译器自动生成从派生的类System.MulticastDelegate类。

这个编译器生成的类包含3种方法,以及: Invoke, BeginInvoke and EndInvoke

所有这三种方法都被标记public virtual extern但有趣的是,类本身被标记sealed

在一个密封类不仅罢工定义为反直觉的,但虚拟的方法是在C#中实际上是非法的。

所以我的问题是,是否有特定的原因,或只是来完成牢记一些假设未来增强这些无害的东西吗?

编辑1:

其原因可能是强制使用的“callVirt” IL操作码而不是“叫”,这样的委托对象正试图执行任何的三种方法之前总是检查空由CLR? 虽然我不明白为什么一个delegate应在这方面的一个特例。

也不是它击中强制使用的性能callvirt (尽管它可能是微不足道的)

编辑2:

新增CIL标签,因为事实证明,确定代表的C#的方式,其实是由CIL标准规定。 标准规定(以下不是全文)

代表应具有System.Delegate的基本类型。 代表应被宣布封闭 ,唯一的成员代表应是既可以作为这里指定的头两个或全部四种方法。 这些方法应被宣布为运行和管理。 他们不应有身体,因为该机构应当由VES自动创建。 可在委托其他方法是从基类库中的类System.Delegate继承。 委托方法是:

  1. 实例构造函数
  2. Invoke方法应是虚拟
  3. BeginInvoke方法,如果存在的话,应是虚拟
  4. 该EndInvoke会方法应虚拟

所以这绝对不是编译过程的副作用或类似其他有趣的编译器输出。

如果标准强调的东西,它必须是一些很好的原因和理由。

所以,现在的问题是为什么为代表CIL标准强调密封和虚拟在同一时间?

是否抓躺在这里?:

他们不应有身体,因为该机构应当由VES自动创建。

他们是标志着虚拟的,这样的VES / CLR生成的主体可以对这些方法的调用执行?

Answer 1:

您正在被你用来看看类型定义的反汇编绊倒。 在IL必须转换回可识别的语言,如C#。 这不是一般的可能与全保真做,对于IL规则是一样的C#语言规则。 这不只是发生为代表,接口实现方法是虚拟的,以及,即使你不声明它在你的C#代码的虚拟。

为了进一步浑水摸鱼,IL实际上允许编译器产生的非虚拟呼叫的虚拟方法,如果它能够确定从代码分析目标对象。 但是,这永远不会发生的委托或接口调用。 和IL允许进行虚拟呼叫到一个非虚方法,一些C#编译器津津有味确实落实保证实例方法不能用null,则调用。

但是,C#的使用是一个聪明的把戏,CLR的设计之后才发现的。 虚拟原意当然是标注方法应与Callvirt被调用。 最终,它并不重要,因为编译器知道的委托和接口的行为,并会一直发出Callvirt。 而实际的方法调用中,假定Callvirt激活CLR代码来实现。



Answer 2:

正如我在我的问题指出,这种密封虚拟异常是实际上是由CIL标准规定。 为什么CIL标准特别提到,委托方法目前还不清楚InvokeBeginInvokeEndInvoke应在同一时间强制密封是虚拟的,而Delegate继承类。

此外,通过SSCLI代码会后,我了解到,JIT编译器的内部优化自动转换任何callvirt一个密封类的与附加空检查正常调用虚方法调用。 这意味着,代表们不遭受任何性能损失时,其调用(或任何其他两个的)方法是通过被称为callvirt指令尽管被标注在IL虚。

当委托的invoke被调用时,CLR自动发出高度优化的身体这种方法,而不是编译IL代码来生成体,其它为“正常”的方法。 这有没有关系被标记virtual的IL。

我还用手工修改IL代码,并重新组装认为虚拟可以从所生成的代表类的IL代码可安全删除验证。 尽管违反了CIL标准正在生成的程序集运行完全正常。

.class private auto ansi beforefieldinit MainApp
       extends [mscorlib]System.Object
{
  .class auto ansi sealed nested private Echo
         extends [mscorlib]System.MulticastDelegate
  {
    .method public hidebysig specialname rtspecialname 
            instance void  .ctor(object 'object',
                                 native int 'method') runtime managed
    {
    } // end of method Echo::.ctor

    .method public hidebysig instance int32  Invoke(int32 i) runtime managed
    {
    } // end of method Echo::Invoke

    .method public hidebysig instance class [mscorlib]System.IAsyncResult 
            BeginInvoke(int32 i,
                        class [mscorlib]System.AsyncCallback callback,
                        object 'object') runtime managed
    {
    } // end of method Echo::BeginInvoke

    .method public hidebysig instance int32  EndInvoke(class [mscorlib]System.IAsyncResult result) runtime managed
    {
    } // end of method Echo::EndInvoke

  } // end of class Echo

  .method public hidebysig static void  Main() cil managed
  {
    .entrypoint
    // Code size       34 (0x22)
    .maxstack  3
    .locals init ([0] class MainApp app,
             [1] class MainApp/Echo dele)
    IL_0000:  nop
    IL_0001:  newobj     instance void MainApp::.ctor()
    IL_0006:  stloc.0
    IL_0007:  ldloc.0
    IL_0008:  ldftn      instance int32 MainApp::DoEcho(int32)
    IL_000e:  newobj     instance void MainApp/Echo::.ctor(object,
                                                           native int)
    IL_0013:  stloc.1
    IL_0014:  ldloc.1
    IL_0015:  ldc.i4.5
    //callvirt can also be replaced by call without affecting functionality
    // since delegate object is essentially not null here
    IL_0016:  callvirt   instance int32 MainApp/Echo::Invoke(int32)
    IL_001b:  call       void [mscorlib]System.Console::WriteLine(int32)
    IL_0020:  nop
    IL_0021:  ret
  } // end of method MainApp::Main

  .method private hidebysig instance int32 
          DoEcho(int32 i) cil managed
  {
    // Code size       7 (0x7)
    .maxstack  1
    .locals init ([0] int32 CS$1$0000)
    IL_0000:  nop
    IL_0001:  ldarg.1
    IL_0002:  stloc.0
    IL_0003:  br.s       IL_0005

    IL_0005:  ldloc.0
    IL_0006:  ret
  } // end of method MainApp::DoEcho

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method MainApp::.ctor

} // end of class MainApp

请注意,我已经转换的虚拟方法到正常实例方法。

由于这种改变IL运行完全正常它证明了在密封委托类标准规定的虚拟方法不是必需的。 它们可以是正常的实例方法为好。

所以,在所有的概率这种异常是要么强调,调用这三个委托方法将在-其实结果在调用其他方法(即运行时多态性就像“正常”的虚方法)或本已如此,以适应将来的某个有关代表假设的增强。



Answer 3:

这是编译过程的副作用。 我不知道这样做的确切原因,有这种行为的更多的例子。 例如,编译静态类成为一个抽象的密封类(所以你不能创建它的一个实例,你不能继承它)。



Answer 4:

这似乎不是具体到代表。 我试过了这个例子:

 public abstract class Base
    {
        public abstract void Test();
    }

    public sealed class Derived : Base
    {
        public override  void Test()
        {
            throw new NotImplementedException();
        }
    }

在ILDASM我得到这个测试()的实现:

.method public hidebysig virtual instance void 
        Test() cil managed
{
  // Code size       7 (0x7)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  newobj     instance void [mscorlib]System.NotImplementedException::.ctor()
  IL_0006:  throw
} // end of method Derived::Test

可能是override关键字是不是一个CLR关键字。



文章来源: Compiler generated sealed class for delegate keyword contains virtual methods