C# emittel-使用ref参数调用方法
我正在尝试用MSIL编写以下代码:C# emittel-使用ref参数调用方法,c#,reflection.emit,C#,Reflection.emit,我正在尝试用MSIL编写以下代码: class ReferenceTestViewModel : BaseViewModel, ITestViewModel { private int id; public int Id { get { return id; } set { this.SetProperty(ref id, value); } } } SetProperty是其祖父的一种方法BaseObservable(Base
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
以下是我如何构建支持字段和属性的信息:
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
以下是stacktrace:
试图加载格式不正确的程序。(0x8007000B)
在DynamicTestViewModel.set_Id(Int32)处
据我所知,这与
SetProperty
是一种通用方法有关
当前调用指令是使用非类型泛型方法引用发出的,因此它本质上是对SetProperty(ref-id,value)
的调用,而不是SetProperty(ref-id,value)
修改调用emit以使用该方法的类型化泛型版本(使用.MakeGeneric(typeof(int))
)应该可以修复它
即
而不是
setIl.Emit(OpCodes.Call, typeof(BaseViewModel).GetMethod(nameof(BaseObservable.SetProperty), BindingFlags.Instance | BindingFlags.NonPublic));
将大型示例简化为几行相关的代码以获得最小的可复制示例是否可行?我建议深入研究
testObject.Id=42代码>Roslyn从C#编译的代码行,用于创建工作基线并使用原始IL和元数据进行分析-不要依赖ILSpy及其bug来指导您的工作。保存生成的模块并在其上运行peverify。我在您的代码中没有看到它,但似乎您正在实现一个接口,因此在某个时候您必须在getter和setter方法上调用TypeBuilder.DefineMethodVerride(…)。@elchido:Shane Delany的回答解决了我的问题,但感谢您的建议。我不知道peverify(实际上我对MSIL^^了解不多),我会看看它!
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));