Question

I googled many questions with same header, but I didn't found answer that fits me.

So i'm just trying to increment a field of my class instance:

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();
    }

why it doesn't work? I guess it concerned with maxstack and local variables, but i dunno really.


about the code: to make sure i wrote and decompiled the same code for another class and here it is:

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++;
    }
}

decompiled:

.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

so it seems to be same

Was it helpful?

Solution

Action does not take any arguments, yet you ldarg one. You probably want an Action<EmitTest>.

The difference to the decompiled code you edited in seems to be that this method is an instance method taking an implicit this argument.

When you call delg(); what instance of EmitTest would you think this runs on? You never specified one.

You can use the overload of CreateDelegate that takes a target. Or use an Action<EmitTest>.


This works:

    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();
        }
    }

Changes:

  1. Bind the delegate to a target
  2. Specify argument types (you told the builder that there are no arguments)
  3. Enable private access
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top