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

在ILGenerator中,调用内部setter失败

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