如何更改动态汇编中构造函数和方法的.maxstack大小

问题描述 投票:0回答:2

我想使用提供的功能动态创建程序集

System.Reflection.Emit
。该程序集应与编译以下 C# 代码所生成的程序集相同:

namespace BasicModule
{
   using Prism.Ioc;
   using Prism.Modularity;

   public class BasicModule : IModule
   {
      public void OnInitialized(IContainerProvider containerProvider)
      {
      }

      public void RegisterTypes(IContainerRegistry containerRegistry)
      {
      }
   }
}

编译上述代码时得到的 IL 代码如下所示:

.class public auto ansi beforefieldinit BasicModule.BasicModule
       extends [mscorlib]System.Object
       implements [Prism]Prism.Modularity.IModule
{
} // end of class BasicModule.BasicModule


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


.method public hidebysig newslot virtual final 
        instance void  OnInitialized(class [Prism]Prism.Ioc.IContainerProvider containerProvider) cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method BasicModule::OnInitialized


.method public hidebysig newslot virtual final 
        instance void  RegisterTypes(class [Prism]Prism.Ioc.IContainerRegistry containerRegistry) cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method BasicModule::RegisterTypes

我使用以下 C# 类代码动态生成程序集:

namespace EmitNewAssembly
{
   using Prism.Ioc;
   using Prism.Modularity;
   using System;
   using System.Globalization;
   using System.Reflection;
   using System.Reflection.Emit;

   internal class TestAssemblyCreator
   {
      private const string FileName = "BasicModule.dll";
      private const string ModuleName = "BasicModule";
      private const string ClassName = "BasicModule";

      private const MethodAttributes InterfaceMethodAttributes =
         MethodAttributes.Public |
         MethodAttributes.HideBySig |
         MethodAttributes.NewSlot |
         MethodAttributes.Virtual |
         MethodAttributes.Final;

      private const TypeAttributes ClassAttributes =
         TypeAttributes.Public |
         TypeAttributes.Class |
         TypeAttributes.BeforeFieldInit;

      public TestAssemblyCreator()
      {
         AssemblyName assemblyName = new AssemblyName()
         {
            Name = "TestAssembly",
            Version = new Version(1, 2, 3, 4),
            CultureInfo = CultureInfo.InvariantCulture,
         };

         var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save);

         var typeBuilder = assemblyBuilder
            .DefineDynamicModule(ModuleName, FileName)
            .DefineType($"{ModuleName}.{ClassName}", ClassAttributes, typeof(object), new Type[] { typeof(IModule) });

         var methodBuilder = typeBuilder.DefineMethod("OnInitialized", InterfaceMethodAttributes, CallingConventions.Standard, typeof(void), new Type[] { typeof(IContainerProvider) });
         methodBuilder.DefineParameter(1, ParameterAttributes.None, "containerProvider");

         var ilGenerator = methodBuilder.GetILGenerator();
         ilGenerator.Emit(OpCodes.Nop);
         ilGenerator.Emit(OpCodes.Ret);

         methodBuilder = typeBuilder.DefineMethod("RegisterTypes", InterfaceMethodAttributes, CallingConventions.Standard, typeof(void), new Type[] { typeof(IContainerRegistry) });
         methodBuilder.DefineParameter(1, ParameterAttributes.None, "containerRegistry");

         ilGenerator = methodBuilder.GetILGenerator();
         ilGenerator.Emit(OpCodes.Nop);
         ilGenerator.Emit(OpCodes.Ret);

         typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

         typeBuilder.CreateType();

         assemblyBuilder.Save(FileName);
      }
   }
}

问题是生成的IL代码略有不同。看起来如下:

.class public auto ansi beforefieldinit BasicModule.BasicModule
       extends [mscorlib]System.Object
       implements [Prism]Prism.Modularity.IModule
{
} // end of class BasicModule.BasicModule


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


.method public hidebysig newslot virtual final 
        instance void  OnInitialized(class [Prism]Prism.Ioc.IContainerProvider containerProvider) cil managed
{
  // Code size       2 (0x2)
  .maxstack  0
  IL_0000:  nop
  IL_0001:  ret
} // end of method BasicModule::OnInitialized


.method public hidebysig newslot virtual final 
        instance void  RegisterTypes(class [Prism]Prism.Ioc.IContainerRegistry containerRegistry) cil managed
{
  // Code size       2 (0x2)
  .maxstack  0
  IL_0000:  nop
  IL_0001:  ret
} // end of method BasicModule::RegisterTypes

首先,生成的构造函数包含不同的

.maxstack
,2 而不是 8,并且缺少
nop
操作码。其次,
OnInitialized
RegisterTypes
.maxstack
都是0而不是8。

我需要如何更改我的 C# 代码,以便生成的程序集包含与原始代码完全相同的 IL 代码?

编辑

这似乎是一个问题的原因是,如果我在 ILSpy 中打开生成的程序集,我将得到以下输出: .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { // Method begins at RVA 0x2070 // Header size: 12 // Code size: 7 (0x7) .maxstack 2 .locals init /* Signature type sequence must have at least one element. */ ( ) /* 0x0000207C 02 */ IL_0000: ldarg.0 /* 0x0000207D 280100000A */ IL_0001: call instance void [mscorlib]System.Object::.ctor() /* 0x00002082 2A */ IL_0006: ret } // end of method BasicModule::.ctor .method public final hidebysig newslot virtual instance void OnInitialized ( class [Prism]Prism.Ioc.IContainerProvider containerProvider ) cil managed { // Method begins at RVA 0x2050 // Header size: 12 // Code size: 2 (0x2) .maxstack 0 .locals init /* Signature type sequence must have at least one element. */ ( ) /* 0x0000205C 00 */ IL_0000: nop /* 0x0000205D 2A */ IL_0001: ret } // end of method BasicModule::OnInitialized .method public final hidebysig newslot virtual instance void RegisterTypes ( class [Prism]Prism.Ioc.IContainerRegistry containerRegistry ) cil managed { // Method begins at RVA 0x2060 // Header size: 12 // Code size: 2 (0x2) .maxstack 0 .locals init /* Signature type sequence must have at least one element. */ ( ) /* 0x0000206C 00 */ IL_0000: nop /* 0x0000206D 2A */ IL_0001: ret } // end of method BasicModule::RegisterTypes

.locals init /* Signature type sequence must have at least one element.

在我看来,事情确实不像应有的那样。

    

c# reflection.emit
2个回答
2
投票
[...]
System.Reflection.Emit

。该程序集应该与编译生成的程序集相同


你可以就此打住,你的假设是无效的。事实上,您不能使用
Reflection.Emit

生成类似于现代 C# 编译器的代码。

有一个更好的选择,

Mono.Cecil

,它会让你走得更远,但你仍然面临着技术的死胡同。你应该为此使用的,以及人们经常用于此类事情的,是

源生成器


0
投票
没有手动设置 .maxstack 的功能 - 这取决于您的代码。我相信 .maxstack = 8 由 C# 编译器插入 - 这是代码对齐。
    IL 代码中的 NOP 指令 - 它只是一个填充符,通过带有调试信息的编译插入。发布版本中没有 NOP。
  1. 源生成器
  2. - 有趣的主题,但正如之前提到的,它仅适用于 Roslyn,所以对 .Framework 说不
  3. 莫诺·塞西尔?!!严重地?!!我从来没有使用过 Mono.Cecil,但我使用过 mono...很多...之后,就我个人而言,我建议在其上贴上 mono* 标签的所有东西,仅在一个码尺的距离处并在这样做时挡住你的鼻子.
  4. Reflection.Emit 几乎被每个版本的 .NET 支持。这是一个基本功能。我怀疑 c# 本身需要它。它已从 .NET Standard 1.* 中删除,但奇迹般地出现在 2.* 中(与 .NET Core 相同)。
  5. emit 唯一的缺点是移动开发,因为动态代码生成被苹果和谷歌禁止。当然,事实上,通过使用发出功能,如果您创建无效的 IL 代码,您很容易损坏应用程序内存。
© www.soinside.com 2019 - 2024. All rights reserved.