如何为ulong.Parse调用发出LDC_I8?

时间:2013-08-07 16:26:09

标签: c# reflection.emit il

我在发送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会崩溃。

2 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:0)

诀窍是使用unchecked关键字将ulong转换为long以使emit操作满意。

  ilGenerator.Emit(OpCodes.Ldc_I8, unchecked((long)ulong.Parse("0")));