[英]EmitIL - Call method with ref parameter
我正在尝试在 MSIL 中编写以下代码:
class ReferenceTestViewModel : BaseViewModel, ITestViewModel
{
private int id;
public int Id
{
get { return id; }
set { this.SetProperty(ref id, value); }
}
}
SetProperty
是其祖父BaseObservable
( BaseViewModel : BaseObservable
) 的一种方法。
所以我编译了这个ReferenceTestViewModel
并使用 ILSpy 得到以下 IL:
.field private int32 id
.method public final hidebysig specialname newslot virtual
instance void set_Id (
int32 'value'
) cil managed
{
// Method begins at RVA 0x230c
// Code size 21 (0x15)
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldarg.0
IL_0003: ldflda int32 ReferenceTestViewModel::id
IL_0008: ldarg.1
IL_0009: ldstr "Id"
IL_000e: call instance void [OtherAssembly]BaseObservable::SetProperty<int32>(!!0&, !!0, string)
IL_0013: nop
IL_0014: ret
} // end of method ReferenceTestViewModel::set_Id
我最终得到了以下 C# 生成器代码。 在阅读之前,请考虑在我尝试调用SetProperty
方法之前,我的整个班级都运行良好。 它只是设置相关的支持字段(参见我的评论:旧方式),但它工作得很好。 所以我很确定错误就在那个部分。
private static void GenerateSetter(TypeBuilder typeBuilder, string propertyName, Type propertyType, PropertyBuilder propertyBuilder, FieldBuilder backingFieldBuilder)
{
MethodBuilder setPropMthdBldr =
typeBuilder.DefineMethod("set_" + propertyName,
MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.HideBySig
| MethodAttributes.Final
| MethodAttributes.Virtual
| MethodAttributes.NewSlot,
null,
new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
// New way : call SetProperty
setIl.Emit(OpCodes.Nop);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldflda, backingFieldBuilder);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Ldstr, propertyName);
setIl.Emit(OpCodes.Call, typeof(BaseViewModel).GetMethod(nameof(BaseObservable.SetProperty), BindingFlags.Instance | BindingFlags.NonPublic));
setIl.Emit(OpCodes.Nop);
setIl.Emit(OpCodes.Ret);
// Old way : simply set the backing field and return
//setIl.Emit(OpCodes.Ldarg_0);
//setIl.Emit(OpCodes.Ldarg_1);
//setIl.Emit(OpCodes.Stfld, backingFieldBuilder);
//setIl.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
有关信息,以下是我构建支持字段和属性的方法:
private static void GenerateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType, ConstructorBuilder ctorBuilder)
{
var fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
var propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
GenerateGetter(typeBuilder, propertyBuilder, fieldBuilder);
GenerateSetter(typeBuilder, propertyName, propertyType, propertyBuilder, fieldBuilder);
}
private static void InitializeProperty(Type propertyType, ConstructorBuilder ctorBuilder, FieldBuilder fieldBuilder)
{
var propertyCtor = propertyType.GetConstructors().First();
var emitIL = ctorBuilder.GetILGenerator();
emitIL.Emit(OpCodes.Ldarg_0);
emitIL.Emit(OpCodes.Newobj, propertyCtor);
emitIL.Emit(OpCodes.Stfld, fieldBuilder);
}
private static void GenerateGetter(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, FieldBuilder backingFieldBuilder)
{
MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod(
"get_" + propertyBuilder.Name,
MethodAttributes.Public
| MethodAttributes.SpecialName
| MethodAttributes.HideBySig
| MethodAttributes.Final
| MethodAttributes.Virtual
| MethodAttributes.NewSlot,
propertyBuilder.PropertyType,
Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, backingFieldBuilder);
getIl.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getPropMthdBldr);
}
最后,这是失败的测试:
var testObject = (ITestViewModel)Activator.CreateInstance(_dynamicType);
var value = testObject.Id; // works fine
testObject.Id = 42; // throw BadImageFormatException
这是堆栈跟踪:
试图加载格式不正确的程序。 (0x8007000B)
在 DynamicITestViewModel.set_Id(Int32)
据我所知,这与SetProperty
作为通用方法有关。
当前调用指令是使用非类型化泛型方法引用发出的,因此它本质上是调用SetProperty<T>(ref id, value)
而不是SetProperty<Int32>(ref id, value)
。
修改调用发出以使用该方法的类型化泛型版本(使用.MakeGeneric(typeof(int))
)应该可以修复它。
IE
setIl.Emit(OpCodes.Call, typeof(BaseViewModel).GetMethod(nameof(BaseObservable.SetProperty), BindingFlags.Instance | BindingFlags.NonPublic).MakeGeneric(typeof(int)));
而不是
setIl.Emit(OpCodes.Call, typeof(BaseViewModel).GetMethod(nameof(BaseObservable.SetProperty), BindingFlags.Instance | BindingFlags.NonPublic));
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.