Reflection Emit:如何生成getter进行比较
Reflection Emit: How to generate getter for comparision
我在以下代码中为 HasChildren 属性 生成 getterMethod 时遇到问题。
有人可以帮我解决这个问题吗?
c# 中的代码
public class Sample
{
public ObservableCollection<Sample> Children { get; set; }
public bool HasChildren { get { return Children?.Count() > 0; } }
}
此处为 methodbuilder 引用了无效的代码。
const string assemblyName = "HasChildrenAssembly";
const string childrenProperty = "Children";
const string hasChildrenProperty = "HasChildren";
const string typeName = "Sample";
const string assemblyFileName = assemblyName + ".dll";
AppDomain domain = AppDomain.CurrentDomain;
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
Type typeOfChildren = typeof(ObservableCollection<>);
Type genericTypeOfChildren = typeOfChildren.MakeGenericType(typeBuilder);
FieldBuilder childrenField = typeBuilder.DefineField($"_{childrenProperty}", genericTypeOfChildren, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(childrenProperty, PropertyAttributes.None, childrenField.FieldType, Type.EmptyTypes);
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder getChildrenMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, childrenField.FieldType, Type.EmptyTypes);
ILGenerator il = getChildrenMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, childrenField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getChildrenMethod);
MethodBuilder setChildrenMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new[] { propertyBuilder.PropertyType });
il = setChildrenMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, childrenField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setChildrenMethod);
MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethods().Single(method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1);
propertyBuilder = typeBuilder.DefineProperty(hasChildrenProperty, PropertyAttributes.None, typeof(bool), Type.EmptyTypes);
MethodBuilder getHasChildrenMethod = typeBuilder.DefineMethod($"get_{hasChildrenProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes);
il = getHasChildrenMethod.GetILGenerator();
var notNullLabel = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getChildrenMethod);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue_S, notNullLabel);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ret);
il.MarkLabel(notNullLabel);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, countMethodInfo);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Cgt);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getHasChildrenMethod);
ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeBuilder });
il = constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, setChildrenMethod);
il.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
var obj1 = Activator.CreateInstance(type, new object[] { null });
var obj2 = Activator.CreateInstance(type, obj1);
assemblyBuilder.Save(assemblyFileName);
提前致谢。
您没有在 Count
方法中指定通用参数。
您必须使用 MakeGenericMethod
指定通用参数是 Sample
您在 notNullLabel
之后还有一个额外的 Ldarg_0
。由于最开始的 dup
操作,此时 this
已经在堆栈中。
这里是一个稍微修改过的版本,可以按您的预期工作:
AppDomain ad = AppDomain.CurrentDomain;
AssemblyBuilder ab = ad.DefineDynamicAssembly(new AssemblyName("toto.dll"), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("toto.dll");
TypeBuilder tb = mb.DefineType("toto.Sample", TypeAttributes.Public | TypeAttributes.Class);
FieldBuilder fb = tb.DefineField("<Children>k__BackingField", typeof(ObservableCollection<>).MakeGenericType(tb), FieldAttributes.Private | FieldAttributes.InitOnly);
fb.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0]));
PropertyBuilder pb = tb.DefineProperty("Children", PropertyAttributes.None, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]);
MethodBuilder getter = tb.DefineMethod("get_Children", MethodAttributes.Public, CallingConventions.HasThis, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]);
getter.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0]));
ILGenerator ilgen = getter.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fb);
ilgen.Emit(OpCodes.Ret);
pb.SetGetMethod(getter);
PropertyBuilder pbhas = tb.DefineProperty("HasChildren", PropertyAttributes.None, typeof(bool), new Type[0]);
MethodBuilder hasgetter = tb.DefineMethod("get_HasChildren", MethodAttributes.Public, CallingConventions.HasThis, typeof(bool), new Type[0]);
ilgen = hasgetter.GetILGenerator();
Label notNullLabel = ilgen.DefineLabel();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, getter);
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Brtrue, notNullLabel);
ilgen.Emit(OpCodes.Pop);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(notNullLabel);
MethodInfo mi = typeof(Enumerable).GetMethods()
.Where(m => m.Name == "Count" && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1)
.First()
.MakeGenericMethod(tb);
ilgen.Emit(OpCodes.Call, mi);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Cgt);
ilgen.Emit(OpCodes.Ret);
pbhas.SetGetMethod(hasgetter);
tb.CreateType();
ab.Save("toto.dll");
我在以下代码中为 HasChildren 属性 生成 getterMethod 时遇到问题。
有人可以帮我解决这个问题吗?
c# 中的代码
public class Sample
{
public ObservableCollection<Sample> Children { get; set; }
public bool HasChildren { get { return Children?.Count() > 0; } }
}
此处为 methodbuilder 引用了无效的代码。
const string assemblyName = "HasChildrenAssembly";
const string childrenProperty = "Children";
const string hasChildrenProperty = "HasChildren";
const string typeName = "Sample";
const string assemblyFileName = assemblyName + ".dll";
AppDomain domain = AppDomain.CurrentDomain;
AssemblyBuilder assemblyBuilder = domain.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName, assemblyFileName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
Type typeOfChildren = typeof(ObservableCollection<>);
Type genericTypeOfChildren = typeOfChildren.MakeGenericType(typeBuilder);
FieldBuilder childrenField = typeBuilder.DefineField($"_{childrenProperty}", genericTypeOfChildren, FieldAttributes.Private);
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(childrenProperty, PropertyAttributes.None, childrenField.FieldType, Type.EmptyTypes);
MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
MethodBuilder getChildrenMethod = typeBuilder.DefineMethod($"get_{propertyBuilder.Name}", getSetAttr, childrenField.FieldType, Type.EmptyTypes);
ILGenerator il = getChildrenMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldfld, childrenField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getChildrenMethod);
MethodBuilder setChildrenMethod = typeBuilder.DefineMethod($"set_{propertyBuilder.Name}", getSetAttr, null, new[] { propertyBuilder.PropertyType });
il = setChildrenMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Stfld, childrenField);
il.Emit(OpCodes.Ret);
propertyBuilder.SetSetMethod(setChildrenMethod);
MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethods().Single(method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1);
propertyBuilder = typeBuilder.DefineProperty(hasChildrenProperty, PropertyAttributes.None, typeof(bool), Type.EmptyTypes);
MethodBuilder getHasChildrenMethod = typeBuilder.DefineMethod($"get_{hasChildrenProperty}", getSetAttr, propertyBuilder.PropertyType, Type.EmptyTypes);
il = getHasChildrenMethod.GetILGenerator();
var notNullLabel = il.DefineLabel();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, getChildrenMethod);
il.Emit(OpCodes.Dup);
il.Emit(OpCodes.Brtrue_S, notNullLabel);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Ret);
il.MarkLabel(notNullLabel);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, countMethodInfo);
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Cgt);
il.Emit(OpCodes.Ret);
propertyBuilder.SetGetMethod(getHasChildrenMethod);
ConstructorBuilder constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, new Type[] { typeBuilder });
il = constructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, typeof(object).GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, setChildrenMethod);
il.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
var obj1 = Activator.CreateInstance(type, new object[] { null });
var obj2 = Activator.CreateInstance(type, obj1);
assemblyBuilder.Save(assemblyFileName);
提前致谢。
您没有在 Count
方法中指定通用参数。
您必须使用 MakeGenericMethod
指定通用参数是Sample
您在 notNullLabel
之后还有一个额外的 Ldarg_0
。由于最开始的 dup
操作,此时 this
已经在堆栈中。
这里是一个稍微修改过的版本,可以按您的预期工作:
AppDomain ad = AppDomain.CurrentDomain;
AssemblyBuilder ab = ad.DefineDynamicAssembly(new AssemblyName("toto.dll"), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = ab.DefineDynamicModule("toto.dll");
TypeBuilder tb = mb.DefineType("toto.Sample", TypeAttributes.Public | TypeAttributes.Class);
FieldBuilder fb = tb.DefineField("<Children>k__BackingField", typeof(ObservableCollection<>).MakeGenericType(tb), FieldAttributes.Private | FieldAttributes.InitOnly);
fb.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0]));
PropertyBuilder pb = tb.DefineProperty("Children", PropertyAttributes.None, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]);
MethodBuilder getter = tb.DefineMethod("get_Children", MethodAttributes.Public, CallingConventions.HasThis, typeof(ObservableCollection<>).MakeGenericType(tb), new Type[0]);
getter.SetCustomAttribute(new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(new Type[0]), new object[0]));
ILGenerator ilgen = getter.GetILGenerator();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Ldfld, fb);
ilgen.Emit(OpCodes.Ret);
pb.SetGetMethod(getter);
PropertyBuilder pbhas = tb.DefineProperty("HasChildren", PropertyAttributes.None, typeof(bool), new Type[0]);
MethodBuilder hasgetter = tb.DefineMethod("get_HasChildren", MethodAttributes.Public, CallingConventions.HasThis, typeof(bool), new Type[0]);
ilgen = hasgetter.GetILGenerator();
Label notNullLabel = ilgen.DefineLabel();
ilgen.Emit(OpCodes.Ldarg_0);
ilgen.Emit(OpCodes.Call, getter);
ilgen.Emit(OpCodes.Dup);
ilgen.Emit(OpCodes.Brtrue, notNullLabel);
ilgen.Emit(OpCodes.Pop);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Ret);
ilgen.MarkLabel(notNullLabel);
MethodInfo mi = typeof(Enumerable).GetMethods()
.Where(m => m.Name == "Count" && m.GetGenericArguments().Length == 1 && m.GetParameters().Length == 1)
.First()
.MakeGenericMethod(tb);
ilgen.Emit(OpCodes.Call, mi);
ilgen.Emit(OpCodes.Ldc_I4_0);
ilgen.Emit(OpCodes.Cgt);
ilgen.Emit(OpCodes.Ret);
pbhas.SetGetMethod(hasgetter);
tb.CreateType();
ab.Save("toto.dll");