在我的应用程序中,我需要动态创建一个包含多个属性的类型。我知道在这种情况下,必须使用ILGenerator为属性的getter和setter方法生成CIL。
更多的是通过反复试验,我终于找到了以下为我生成setter方法的代码:
MethodBuilder setMethod = customTypeBuilder.DefineMethod(propertyName + "_set", MethodAttributes.Public | MethodAttributes.HideBySig, null, new Type[] {propertyType});
ILGenerator setIlGenerator = setMethod.GetILGenerator();
setIlGenerator.Emit(OpCodes.Ldarg_0);
setIlGenerator.Emit(OpCodes.Ldarg_1);
setIlGenerator.Emit(OpCodes.Stfld, backingField);
setIlGenerator.Emit(OpCodes.Ret);
代码运行良好,但有一件事我不明白。 为什么需要调用'Ldarg_0'指令?
我知道它引用了方法的隐式第一个参数,即“this”引用,因此setter的实际值存储在第二个参数中。我认为仅调用Ldarg_1指令就足够了,这会将第二个参数推送到堆栈(最后,在setter中,我不需要检查“this”引用,所以我不需要对它做任何事情),但这会导致在我尝试设置属性的值时抛出TargetInvocationException。
谢谢!
答案 0 :(得分:4)
如果你没有将“this”值推到堆栈上,Stfld
将如何知道哪个对象的字段会改变?您可能正在尝试编写这样的setter:
public int Bizarre
{
set { otherObject.Field = value; }
}
基本上,Stfld
是documented在堆栈上需要两个值:一个用于新值的“目标”,另一个用于值本身。无可否认,ECMA 335中的堆栈转换图更清晰:
…, obj, value => …,
换句话说:“stfld将弹出堆栈中的前两个元素”。