在 ILGenerator 中,对内部 setter 的调用失败
为了能够在运行时操作属性,我尝试构建一个通用包装器,它将所有公共/非公共、静态/实例属性转换为 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;
}
}
To be able to manipulate properties at runtime, I am trying to build a generic wrapper that would convert all public/non-public , static/instance properties into public instance properties visible in PropertyGrid control.
The code below works fine for public setters and getters (both static and instance), yet fails for, say, setters having internal scope.
Any help is greatly appreciated.
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;
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这会做到这一点
This will do that