C#,如何在 mvvmlight 风格中使用 Reflection.Emit 属性 getter setter 动态创建
C#, How to dynamically create with Reflection.Emit property getter setter in mvvmlight style
如何创建 属性 如下 Reflection.Emit
private string _Name;
public override string Name{ get => _Name; set => Set(ref _Name, value); }
我用 Reflection.Emmit
试过了
private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
// propertyName = "Name";
// propertyType = typeof(string);
// private string _Name;
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
// public string Name {get; set;}
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
// get;
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
// get => _Name
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
// set;
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
var MvvmLightSetMethod = typeof(ObservableObject)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.Name == "Set").ToList()[2];
// set => Set(ref _Name, value);
setIl.Emit(OpCodes.Mkrefany, fieldBuilder);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, propertyName);
setIl.Emit(OpCodes.Call, MvvmLightSetMethod);
setIl.Emit(OpCodes.Ret);
// public string Name {get; set;}
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
我的问题
如果我 运行 此代码并调用 setter 我在 set_Name(string)
中得到 "Invalid class token exception"
你调用 Set<T>
的 IL 是完全错误的。
Set<T>
具有以下签名:
protected bool Set<T>(
ref T field,
T newValue,
[CallerMemberName] string propertyName = null)
请记住,Type.GetMethods
编写的方法 return 的顺序是未定义的,可以更改。 不要 简单地对此进行索引 - 确保通过指定其签名来获得正确的方法。它也是一种通用方法,因此您需要调用 MakeGenericMethod
.
我也不知道你想用 Mkrefany
做什么。看起来您正在尝试执行 ref this.field
,但已通过 Ldflda
.
完成
最后,Set<T>
return 是一个值,您需要先将其弹出 return。
把所有这些放在一起,你会得到这个:
var setMethod = (from method in typeof(ObservableObject).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
where method.Name == "Set"
let parameters = method.GetParameters()
where parameters.Length == 3 &&
parameters[0].ParameterType.IsByRef &&
parameters[0].ParameterType.ContainsGenericParameters &&
parameters[1].ParameterType.ContainsGenericParameters &&
parameters[2].ParameterType == typeof(string)
select method).Single().MakeGenericMethod(propertyType);
// set => Set(ref _Name, value);
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' -- we'll use it when calling Set later
// Stack: [this]
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' again, for accessing 'this.field'
// Stack: [this, this]
setIl.Emit(OpCodes.Ldflda, fieldBuilder); // Load the address of 'this.field'
// Stack: [this, ref this.field]
setIl.Emit(OpCodes.Ldarg_1); // Load 'value'
// Stack: [this, ref this.field, value]
setIl.Emit(OpCodes.Ldstr, propertyName); // Load 'propertyName'
// Stack: [this, ref this.field, value, propertyName]
setIl.Emit(OpCodes.Call, setMethod);
// Stack: [bool]
setIl.Emit(OpCodes.Pop); // Pop the returned value
// Stack: []
setIl.Emit(OpCodes.Ret);
SharpLab 是编写 IL 的无价之宝。
如何创建 属性 如下 Reflection.Emit
private string _Name;
public override string Name{ get => _Name; set => Set(ref _Name, value); }
我用 Reflection.Emmit
试过了 private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
// propertyName = "Name";
// propertyType = typeof(string);
// private string _Name;
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);
// public string Name {get; set;}
PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
// get;
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();
// get => _Name
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
// set;
MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });
ILGenerator setIl = setPropMthdBldr.GetILGenerator();
var MvvmLightSetMethod = typeof(ObservableObject)
.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => m.Name == "Set").ToList()[2];
// set => Set(ref _Name, value);
setIl.Emit(OpCodes.Mkrefany, fieldBuilder);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, propertyName);
setIl.Emit(OpCodes.Call, MvvmLightSetMethod);
setIl.Emit(OpCodes.Ret);
// public string Name {get; set;}
propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}
我的问题
如果我 运行 此代码并调用 setter 我在 set_Name(string)
中得到 "Invalid class token exception"你调用 Set<T>
的 IL 是完全错误的。
Set<T>
具有以下签名:
protected bool Set<T>(
ref T field,
T newValue,
[CallerMemberName] string propertyName = null)
请记住,Type.GetMethods
编写的方法 return 的顺序是未定义的,可以更改。 不要 简单地对此进行索引 - 确保通过指定其签名来获得正确的方法。它也是一种通用方法,因此您需要调用 MakeGenericMethod
.
我也不知道你想用 Mkrefany
做什么。看起来您正在尝试执行 ref this.field
,但已通过 Ldflda
.
最后,Set<T>
return 是一个值,您需要先将其弹出 return。
把所有这些放在一起,你会得到这个:
var setMethod = (from method in typeof(ObservableObject).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly)
where method.Name == "Set"
let parameters = method.GetParameters()
where parameters.Length == 3 &&
parameters[0].ParameterType.IsByRef &&
parameters[0].ParameterType.ContainsGenericParameters &&
parameters[1].ParameterType.ContainsGenericParameters &&
parameters[2].ParameterType == typeof(string)
select method).Single().MakeGenericMethod(propertyType);
// set => Set(ref _Name, value);
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' -- we'll use it when calling Set later
// Stack: [this]
setIl.Emit(OpCodes.Ldarg_0); // Load 'this' again, for accessing 'this.field'
// Stack: [this, this]
setIl.Emit(OpCodes.Ldflda, fieldBuilder); // Load the address of 'this.field'
// Stack: [this, ref this.field]
setIl.Emit(OpCodes.Ldarg_1); // Load 'value'
// Stack: [this, ref this.field, value]
setIl.Emit(OpCodes.Ldstr, propertyName); // Load 'propertyName'
// Stack: [this, ref this.field, value, propertyName]
setIl.Emit(OpCodes.Call, setMethod);
// Stack: [bool]
setIl.Emit(OpCodes.Pop); // Pop the returned value
// Stack: []
setIl.Emit(OpCodes.Ret);
SharpLab 是编写 IL 的无价之宝。