【问题标题】:Emit Operation could destabilize the runtime for incrementing field发射操作可能会破坏递增字段的运行时间
【发布时间】:2014-02-25 02:48:27
【问题描述】:

我用相同的标题搜索了许多问题,但没有找到适合我的答案。

所以我只是想增加我的类实例的一个字段:

class EmitTest
{
    private int _calls = 0;

    public EmitTest()
    {
        var callsFieldInfo = GetType().GetField("_calls", BindingFlags.NonPublic | BindingFlags.Instance);
        Debug.Assert(callsFieldInfo != null, "callsFieldInfo != null");

        var dynMethod = new DynamicMethod(new Guid().ToString(), typeof(void), null);
        var ilGenerator = dynMethod.GetILGenerator();
        ilGenerator.Emit(OpCodes.Nop);
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.Emit(OpCodes.Dup);
        ilGenerator.Emit(OpCodes.Ldfld, callsFieldInfo);
        ilGenerator.Emit(OpCodes.Ldc_I4_1);
        ilGenerator.Emit(OpCodes.Add);
        ilGenerator.Emit(OpCodes.Stfld, callsFieldInfo);
        ilGenerator.Emit(OpCodes.Ret);

        Action delg = (Action)dynMethod.CreateDelegate(typeof(Action));
        delg();
    }
}

...

    static void Main(string[] args)
    {
        var test = new EmitTest();
    }

为什么它不起作用?我猜它与 maxstack 和局部变量有关,但我真的不知道。


关于代码:确保我为另一个类编写和反编译了相同的代码,这里是:

class Program
{
    private int i = 0;
    static void Main(string[] args)
    {
        var test = new EmitTest();
        var prog = new Program();
        prog.Foo();
    }

    private void Foo()
    {
        i++;
    }
}

反编译:

.method private hidebysig instance void  Foo() cil managed
{
  // Размер кода:       16 (0x10)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  dup
  IL_0003:  ldfld      int32 ConsoleApplication97.Program::i
  IL_0008:  ldc.i4.1
  IL_0009:  add
  IL_000a:  stfld      int32 ConsoleApplication97.Program::i
  IL_000f:  ret
} // end of method Program::Foo

好像是一样的

【问题讨论】:

    标签: c# .net reflection code-generation reflection.emit


    【解决方案1】:

    Action 不接受任何参数,但你 ldarg 一个。你可能想要Action<EmitTest>

    与您编辑的反编译代码的区别似乎是该方法是一个实例方法,采用隐式this 参数。

    当您调用delg(); 时,您认为EmitTest 的哪个实例会运行?你从来没有指定过。

    您可以使用带有目标的CreateDelegate 的重载。或者使用Action<EmitTest>


    这行得通:

        class EmitTest
        {
            private int _calls = 0;
    
            public EmitTest()
            {
                var callsFieldInfo = GetType().GetField("_calls", BindingFlags.NonPublic | BindingFlags.Instance);
                Debug.Assert(callsFieldInfo != null, "callsFieldInfo != null");
    
                var dynMethod = new DynamicMethod(new Guid().ToString(), typeof(void), new[] { typeof(EmitTest) } /*added*/, true /*added*/);
                var ilGenerator = dynMethod.GetILGenerator();
                ilGenerator.Emit(OpCodes.Nop);
                ilGenerator.Emit(OpCodes.Ldarg_0);
                ilGenerator.Emit(OpCodes.Dup);
                ilGenerator.Emit(OpCodes.Ldfld, callsFieldInfo);
                ilGenerator.Emit(OpCodes.Ldc_I4_1);
                ilGenerator.Emit(OpCodes.Add);
                ilGenerator.Emit(OpCodes.Stfld, callsFieldInfo);
                ilGenerator.Emit(OpCodes.Ret);
    
                Action delg = (Action)dynMethod.CreateDelegate(typeof(Action), this /*added*/);
                delg();
            }
        }
    

    变化:

    1. 将委托绑定到目标
    2. 指定参数类型(您告诉构建器没有参数)
    3. 启用私人访问

    【讨论】:

    • 如果方法有签名void Foo(void),为什么我应该使用Action<EmitTest>
    • 我希望参数最终会成为 this 指针。如果这不起作用,请像我说的那样绑定目标。另外,你为什么不试试这两个想法呢?
    • 你的意思是实例方法总是有一个隐藏参数'this'?好主意,但不,如果这样做会失败并出现签名异常。
    • CreateDelegate 重载目标是做什么的?你为什么不测试它,不说什么?
    • 我添加了一个工作版本。反射发射 API 并不是很容易使用。你必须把很多事情做好,否则如果没有好的信息,它就会失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-05
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    • 2010-09-27
    • 1970-01-01
    相关资源
    最近更新 更多