2011-10-05 33 views
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; 
    } 
} 
+0

輔助規則是由CLR執行,你不能用EMIT繞過它。 –

+0

嗨漢斯!糾正我,如果我錯了,但它可以調用非公開的方法使用反射...我錯過了什麼? – vabram

+0

是的,假設適當的訪問權限,反射允許違反規則。它與Reflection.Emit沒有任何關係,它不使用IL。 –

回答

1

這將做到這一點

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; 
}