【问题标题】:Mono.Cecil auto-implemented property accessing backing fieldMono.Cecil 自动实现的属性访问支持字段
【发布时间】:2015-08-19 17:29:21
【问题描述】:

我正在使用Mono.Cecil 在自动实现的属性设置器中注入一些IL 代码。问题是,我可以从TypeDefinition.Fields 对象中获取对它的引用,但是当我使用该引用注入ldfld 指令(在ldarg.0 指令ofc 之后)时,它会导致应用程序中断,并引发CLR invalid program detected 异常。我还尝试反编译ILSpy 并得到异常Mono.Cecil argument out of range exepction in get_Item(int32) method。所以这就像我试图在编译器创建它之前访问支持字段,但不知何故Mono.Cecil 在加载程序集时可以看到它。

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

这是 setter 在注入前的样子:

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

这里是注入代码:

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

这是我得到的 IL:

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

所以注入代码没有中断,存在对支持字段的引用,但在IL 中看起来根本没有支持字段。

【问题讨论】:

    标签: c# code-injection mono.cecil


    【解决方案1】:

    问题解决了!这不是财产,而是IL 的工作方式。我用代码创建了一个完整的属性:

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

    我在ILSpyIL 中打开程序集代码就像我注入到自动实现的属性中一样。但是后来我反编译了那个IL,看看反编译后C#的代码是什么样子的,代码是什么样子的:

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

    所以,问题在于方法堆栈框架上缺少局部变量。在方法中插入局部变量后,一切正常。

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

    【讨论】:

      猜你喜欢
      • 2019-04-03
      • 2012-02-07
      • 2015-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-26
      相关资源
      最近更新 更多