在ILGenerator中,调用内部setter失败
本文关键字:内部 setter 失败 调用 ILGenerator | 更新日期: 2023-09-27 18:10:26
为了能够在运行时操作属性,我试图构建一个通用包装器,将所有公共/非公共,静态/实例属性转换为PropertyGrid控件中可见的公共实例属性。
下面的代码对于公共setter和getter(静态和实例)可以正常工作,但是对于具有内部作用域的setter就会失败。
感谢您的帮助。
public static class PropertyWrapper<T> where T : class
{
public const BindingFlags DefaultBindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Static
| BindingFlags.Instance;
public static object Instance(T obj)
{
return Instance(obj, true, DefaultBindingFlags);
}
public static object Instance(T obj, bool readOnly)
{
return Instance(obj, readOnly, DefaultBindingFlags);
}
public static object Instance(T wrappedObject, bool readOnly, BindingFlags bindingFlags)
{
string commonName = "propertyWrapperModule.dll";
FieldAttributes fieldAttributes = FieldAttributes.Public;
string wrapperTypeName = wrappedObject.GetType().Name + "_WRAPPER";
AssemblyName assemblyName = new AssemblyName { Name = "commonName" };
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(commonName);
TypeBuilder typeBuilder = moduleBuilder.DefineType(wrapperTypeName, TypeAttributes.Public | TypeAttributes.Class);
var objProperties = wrappedObject.GetType().GetProperties(bindingFlags);
foreach (var objProperty in objProperties)
{
// Field
FieldBuilder fieldBuilder = typeBuilder.DefineField(objProperty.Name, objProperty.PropertyType, fieldAttributes);
// Property
PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(objProperty.Name,
PropertyAttributes.None,
objProperty.PropertyType,
new Type[] { objProperty.PropertyType });
// Define Getter
if (objProperty.CanRead)
{
MethodInfo objGetterMethodInfo = objProperty.GetGetMethod(true);
if (objGetterMethodInfo != null)
{
MethodBuilder getterMethodBuilder = DefineGetter(objGetterMethodInfo, typeBuilder, fieldBuilder);
propertyBuilder.SetGetMethod(getterMethodBuilder);
}
}
// Define Setter
if (objProperty.CanWrite)
{
MethodInfo objSetterMethodInfo = objProperty.GetSetMethod(true);
if (objSetterMethodInfo != null)
{
MethodBuilder methodBuilderSetter = DefineSetter(objSetterMethodInfo, typeBuilder, fieldBuilder); // , objectType);
propertyBuilder.SetSetMethod(methodBuilderSetter);
}
}
}
Type wrapperType = typeBuilder.CreateType();
var wrapperObject = Activator.CreateInstance(wrapperType);
// Test
var wrapperProperties = wrapperType.GetProperties();
// Save assembly
assemblyBuilder.Save(commonName);
return wrapperObject;
} // public object CreateNewObject(T obj)
private static MethodBuilder DefineGetter(MethodInfo getterMethodInfo, TypeBuilder typeBuilder, FieldBuilder fieldBuilder) // Type objectType)
{
MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName; // | MethodAttributes.Virtual;
MethodBuilder getterMethodBuilder = typeBuilder.DefineMethod
(
getterMethodInfo.Name,
attributes,
getterMethodInfo.ReturnType,
Type.EmptyTypes
);
// Generate IL
ILGenerator ilGenerator = getterMethodBuilder.GetILGenerator();
ilGenerator.DeclareLocal(fieldBuilder.FieldType);
ilGenerator.Emit(OpCodes.Nop);
if (!getterMethodInfo.IsStatic)
{
ilGenerator.Emit(OpCodes.Ldarg_0);
}
ilGenerator.EmitCall(OpCodes.Callvirt, getterMethodInfo, null);
ilGenerator.Emit(OpCodes.Stloc_0);
// http://stackoverflow.com/questions/6766839/using-br-s-opcode-to-point-to-next-instruction-using-reflection-emit-label
Label targetInstruction = ilGenerator.DefineLabel();
ilGenerator.Emit(OpCodes.Br_S, targetInstruction);
ilGenerator.MarkLabel(targetInstruction);
ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ret);
return getterMethodBuilder;
}
private static MethodBuilder DefineSetter(MethodInfo setterMethodInfo, TypeBuilder typeBuilder, FieldBuilder fieldBuilder)
{
MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName;
MethodBuilder setterMethodBuilder = typeBuilder.DefineMethod
(
setterMethodInfo.Name,
attributes,
null,
new Type[] { fieldBuilder.FieldType }
);
// Generate IL
ILGenerator ilGenerator = setterMethodBuilder.GetILGenerator();
ilGenerator.Emit(OpCodes.Nop);
if (!setterMethodInfo.IsStatic)
{
ilGenerator.Emit(OpCodes.Ldarg_0);
}
ilGenerator.Emit(OpCodes.Ldarg_1);
ilGenerator.EmitCall(OpCodes.Callvirt, setterMethodInfo, null);
ilGenerator.Emit(OpCodes.Ret);
return setterMethodBuilder;
}
}
public static Type BuildWrapper(object targetObject)
{
Type targetWrapType = targetObject.GetType();
string nameOfDLL = "magicWrapper.dll";
string nameOfAssembly = "magic_Assembly";
string nameOfModule = "magic_Module";
string nameOfType = "magic_Type";
System.Reflection.AssemblyName assemblyName = new System.Reflection.AssemblyName {Name = nameOfAssembly};
System.Reflection.Emit.AssemblyBuilder assemblyBuilder =
System.Threading.Thread.GetDomain().DefineDynamicAssembly(assemblyName,
System.Reflection.Emit.AssemblyBuilderAccess.
RunAndSave);
System.Reflection.Emit.ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(nameOfModule, nameOfDLL);
System.Reflection.Emit.TypeBuilder typeBuilder = moduleBuilder.DefineType(nameOfType,
System.Reflection.TypeAttributes.
Public |
System.Reflection.TypeAttributes.Class);
System.Reflection.Emit.FieldBuilder targetWrapedObjectField =
typeBuilder.DefineField("_" + targetWrapType.FullName.Replace(".", ""), targetWrapType,
System.Reflection.FieldAttributes.Private);
System.Reflection.MethodAttributes constructorAttributes = System.Reflection.MethodAttributes.Public;
Type objType = Type.GetType("System.Object");
System.Reflection.ConstructorInfo objCtor = objType.GetConstructor(new Type[0]);
System.Reflection.Emit.ConstructorBuilder constructorBuilder =
typeBuilder.DefineConstructor(constructorAttributes, System.Reflection.CallingConventions.Standard,
new Type[] {targetWrapType});
System.Reflection.Emit.ILGenerator ilConstructor = constructorBuilder.GetILGenerator();
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Call, objCtor);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Nop);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Stfld, targetWrapedObjectField);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Nop);
ilConstructor.Emit(System.Reflection.Emit.OpCodes.Ret);
System.Reflection.MethodAttributes propertyAttributes = System.Reflection.MethodAttributes.Public |
System.Reflection.MethodAttributes.HideBySig |
System.Reflection.MethodAttributes.SpecialName;
foreach (var prop in targetObject.GetType().GetFields())
{
string Name = prop.Name;
Type DataType = prop.FieldType;
System.Reflection.Emit.PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(Name,
System.Reflection.
PropertyAttributes.
SpecialName,
DataType, null);
System.Reflection.Emit.MethodBuilder methodBuilderGetter = typeBuilder.DefineMethod("get_" + Name,
propertyAttributes,
DataType, new Type[] {});
System.Reflection.Emit.MethodBuilder methodBuilderSetter = typeBuilder.DefineMethod("set_" + Name,
propertyAttributes,
typeof (void),
new Type[] {DataType});
System.Reflection.Emit.ILGenerator ilGeneratorGetter = methodBuilderGetter.GetILGenerator();
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldfld, targetWrapedObjectField);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldfld, prop);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ret);
System.Reflection.Emit.ILGenerator ilGeneratorSetter = methodBuilderSetter.GetILGenerator();
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldfld, targetWrapedObjectField);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Stfld, prop);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ret);
propertyBuilder.SetGetMethod(methodBuilderGetter);
propertyBuilder.SetSetMethod(methodBuilderSetter);
}
System.Collections.Generic.List<System.Reflection.PropertyInfo> PropertieList = new List<PropertyInfo>();
PropertieList.AddRange(targetObject.GetType().GetProperties(BindingFlags.Public));
PropertieList.AddRange(targetObject.GetType().GetProperties(BindingFlags.NonPublic));
foreach (var prop in PropertieList)
{
string Name = prop.Name;
Type DataType = prop.PropertyType;
System.Reflection.Emit.PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(Name,
System.Reflection.
PropertyAttributes.
SpecialName,
DataType, null);
System.Reflection.Emit.MethodBuilder methodBuilderGetter = typeBuilder.DefineMethod("get_" + Name,
propertyAttributes,
DataType, new Type[] {});
System.Reflection.Emit.MethodBuilder methodBuilderSetter = typeBuilder.DefineMethod("set_" + Name,
propertyAttributes,
typeof (void),
new Type[] {DataType});
System.Reflection.Emit.ILGenerator ilGeneratorGetter = methodBuilderGetter.GetILGenerator();
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldfld, targetWrapedObjectField);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Callvirt, prop.GetGetMethod());
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Stloc_0);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ldloc_0);
ilGeneratorGetter.Emit(System.Reflection.Emit.OpCodes.Ret);
System.Reflection.Emit.ILGenerator ilGeneratorSetter = methodBuilderSetter.GetILGenerator();
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ldfld, targetWrapedObjectField);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ldarg_1);
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Callvirt, prop.GetSetMethod());
ilGeneratorSetter.Emit(System.Reflection.Emit.OpCodes.Ret);
propertyBuilder.SetGetMethod(methodBuilderGetter);
propertyBuilder.SetSetMethod(methodBuilderSetter);
}
// Yes! you must do this, it should not be needed but it is!
Type dynamicType = typeBuilder.CreateType();
// Save to file
assemblyBuilder.Save(nameOfDLL);
return dynamicType;
}