简体   繁体   中英

Mono.Cecil auto-implemented property accessing backing field

I am using Mono.Cecil to inject some IL code in auto-implemented property setter. The problem is, i can get reference to it from TypeDefinition.Fields object but when i inject ldfld instruction (after ldarg.0 instruction ofc) with that reference it causes application to break, and CLR invalid program detected exception is raised. I also tried to decompile ILSpy and got exception Mono.Cecil argument out of range exepction in get_Item(int32) method . So it's like i am trying to access backing field before it's created by compiler, but somehow Mono.Cecil can see it when loads assembly.

public class ILTesting
{
    public int Counter { get; set; }
} 

This is how setter looks like before injection:

IL_0000: ldarg.0
IL_0001: ldarg.1
IL_0002: stfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0007: ret

Here is injection code:

var fieldName = "<" + property.Name + ">k__BackingField";
var fieldRef = ilTestType.Fields.Single(field => field.Name == fieldName);
var setterInstruction = property.SetMethod.Body.Instructions;

setterInstructions.Insert(0, Instruction.Create(OpCodes.Brfalse_S, setterInstructions.Last()));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldloc_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Stloc_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ceq));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldc_I4_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ceq));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_1));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldfld, reference));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Ldarg_0));
setterInstructions.Insert(0, Instruction.Create(OpCodes.Nop));

And this is IL i get:

IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0007: ldarg.1
IL_0008: ceq
IL_000a: ldc.i4.0
IL_000b: ceq
IL_000d: stloc.0
IL_000e: ldloc.0
IL_000f: brfalse.s IL_001a

IL_0011: nop
IL_0012: ldarg.0
IL_0013: ldarg.1
IL_0014: stfld int32 SyringeWpfTest.ILTesting::'<Counter>k__BackingField'
IL_0019: nop

IL_001a: ret

So injection code didn't break, reference to backing field exists, but in IL looks like there is not backing field at all.

Problem solved! It's wasn't property, it was the way IL works. I made a full property with code:

private int _counter;

public int Counter
{
     get { return _counter; }
     set
     {
          if (_counter != value)
          {
               _counter = value;
               NotifyUI();
          }
      }
}

I opened assembly in ILSpy and IL code was just like i injected to auto-implemented property. But then i decompiled that IL to see what C# code looks like after decompileing, and code looked like:

private int _counter;

public int Counter
{
     get { return _counter; }
     set
     {
          bool flag = _counter != value; //THIS THING MADE MY LIFE SO HARD FOR A FEW DAYS!
          if (flag)
          {
               _counter = value;
               NotifyUI();
          }
      }
}

So, problem was missing local variable on methods stack frame. After i inserted local variable in method, everything work perfect.

myProperty.SetMethod.Body.Variables.Add(new VariableDefinition(assembly.MainModul.Import(typeof(bool)));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM