3

我在发出 IL 来设置 uint64 属性值时遇到问题。下面是一些重现该问题的最小代码。

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ConsoleApplication1
{
  class Program
  {
    static void Main(string[] args)
    {
      AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
        new AssemblyName("test"), AssemblyBuilderAccess.RunAndSave);
      ModuleBuilder m_moduleBuilder = assemblyBuilder.DefineDynamicModule("test.dll",
        "test.dll");
      TypeBuilder typeBuilder = m_moduleBuilder.DefineType("Class1",
        TypeAttributes.Public |
        TypeAttributes.Class |
        TypeAttributes.AutoClass |
        TypeAttributes.AnsiClass |
        TypeAttributes.BeforeFieldInit |
        TypeAttributes.AutoLayout, null);

      FieldBuilder fieldBuilder = typeBuilder.DefineField("m_prop1",
          typeof(ulong), FieldAttributes.Private);

      MethodBuilder getMethodBuilder = typeBuilder.DefineMethod(
        "get_prop1",
        MethodAttributes.Public | MethodAttributes.SpecialName |
        MethodAttributes.HideBySig,
        typeof(ulong), Type.EmptyTypes);
      ILGenerator getIlGen = getMethodBuilder.GetILGenerator();
      getIlGen.Emit(OpCodes.Ldarg_0);
      getIlGen.Emit(OpCodes.Ldfld, fieldBuilder);
      getIlGen.Emit(OpCodes.Ret);

      MethodBuilder setMethodBuilder = typeBuilder.DefineMethod(
        "set_prop1",
        MethodAttributes.Public | MethodAttributes.SpecialName |
        MethodAttributes.HideBySig,
        null, new[] { typeof(ulong) });
      ILGenerator setIlGen = setMethodBuilder.GetILGenerator();
      setIlGen.Emit(OpCodes.Ldarg_0);
      setIlGen.Emit(OpCodes.Ldarg_1);
      setIlGen.Emit(OpCodes.Stfld, fieldBuilder);
      setIlGen.Emit(OpCodes.Ret);

      PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(
        "prop1", PropertyAttributes.HasDefault, typeof(ulong),
        null);
      propertyBuilder.SetGetMethod(getMethodBuilder);
      propertyBuilder.SetSetMethod(setMethodBuilder);

      ConstructorBuilder constructorBuilder =
        typeBuilder.DefineConstructor(MethodAttributes.Public,
        CallingConventions.Standard, Type.EmptyTypes);
      ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
      ilGenerator.Emit(OpCodes.Ldarg_0);
      ilGenerator.Emit(OpCodes.Call,
        typeBuilder.BaseType.GetConstructor(Type.EmptyTypes));
      ilGenerator.Emit(OpCodes.Ldarg_0);
      ilGenerator.Emit(OpCodes.Ldc_I8, ulong.Parse("0"));
      ilGenerator.Emit(OpCodes.Call, propertyBuilder.GetSetMethod());
      ilGenerator.Emit(OpCodes.Ret);

      Type class1Type = typeBuilder.CreateType();

      assemblyBuilder.Save("test.dll");
    }
  }
}

如果您在创建的 .dll 上运行 peverify,您将获得以下信息:

[IL]: Error: [C:\code\ConsoleApplication1\ConsoleApplication1\bin\Debug\test.dl
 : Class1::.ctor][offset 0x00000010] Unrecognized local variable number.
1 Error(s) Verifying test.dll

如果你反汇编构造函数,它看起来如下:

.method public specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       18 (0x12)
  .maxstack  4
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  ldc.i8     0x22800000000
  IL_0010:  ldloc.0
  IL_0011:  ret
} // end of method Class1::.ctor

那么为什么会ilGenerator.Emit(OpCodes.Ldc_I8, ulong.Parse("0"));变成 IL_0007: ldc.i8 0x22800000000以及属性集调用发生了什么?

将 ulong.Parse 更改为 long.Parse 可以创建一个没有这些错误的程序集,但是如果我给它提供一个大于 long.MaxValue 的值,long.Parse 将会崩溃。

4

2 回答 2

4

由于缺少-aware 重载,该特定调用Emit最终使用接受 a的重载,正如您在发布原始问题后发现的那样。您在 ILDASM 中看到的看起来很有趣的常量主要是因为重载只会为 0 值发出 4 个字节,但预期会有一个 8 字节的常量,因此它将以下操作码解释为常量的高阶字节(这也是解释了为什么属性访问器调用显然被完全不同的操作码替换)。floatulong

于 2013-08-07T16:56:03.953 回答
0

诀窍是使用 unchecked 关键字将 ulong 转换为 long 以使发射操作愉快。

  ilGenerator.Emit(OpCodes.Ldc_I8, unchecked((long)ulong.Parse("0")));
于 2013-08-07T16:46:59.687 回答