0
爲了能夠在運行時操作屬性,我試圖構建一個通用包裝器,它將所有公共/非公共,靜態/實例屬性轉換爲PropertyGrid控件中可見的公共實例屬性。在ILGenerator中,調用內部設置程序失敗
以下代碼適用於公共設置者和獲取者(包括靜態和實例),但對於具有內部作用域的setters失敗。
任何幫助是極大的讚賞。
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;
}
}
輔助規則是由CLR執行,你不能用EMIT繞過它。 –
嗨漢斯!糾正我,如果我錯了,但它可以調用非公開的方法使用反射...我錯過了什麼? – vabram
是的,假設適當的訪問權限,反射允許違反規則。它與Reflection.Emit沒有任何關係,它不使用IL。 –