如何修改此代码以使用getter / setter?

时间:2020-07-05 13:44:33

标签: c# wpf rdlc cil reflection.emit

我尝试并未能适应此代码。这是来自C#语言汇编的动态类生成器。我不能使用DynamicObjects,因为RDLC报表不适用于System.Dynamic中的任何类,但不能用于由程序集生成的类。

我想做的是将属性定义从私有变量更改为get and set方法。如果它可以与两个函数一起使用,则不必成为属性。

下面的代码已注释了我需要更改的部分。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using System.Dynamic;

namespace PropertyBuilderExample
{
    public class MyClassParent : DynamicObject
    {
        private dynamic _hostElement;
        public dynamic ReadProperty(string name)
        {
            return hostElement[name];
        }
        public dynamic WriteProperty(string name, dynamic value)
        {
            return _hostElement[name] = value;
        }
    }
    public class MyClassBuilder
    {
        AssemblyName asemblyName;
        public MyClassBuilder(string ClassName)
        {
            this.asemblyName = new AssemblyName(ClassName);
        }
        public object CreateObject(string[] PropertyNames, Type[] Types)
        {
            if (PropertyNames.Length != Types.Length)
            {
                Console.WriteLine("The number of property names should match their corresopnding types number");
            }

            TypeBuilder DynamicClass = this.CreateClass();
            this.CreateConstructor(DynamicClass);
            for (int ind = 0; ind < PropertyNames.Count(); ind++)
                CreateProperty(DynamicClass, PropertyNames[ind], Types[ind]);
            Type type = DynamicClass.CreateType();

            return Activator.CreateInstance(type);
        }
        private TypeBuilder CreateClass()
        {
            AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(this.asemblyName, AssemblyBuilderAccess.Run);
            ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
            TypeBuilder typeBuilder = moduleBuilder.DefineType(this.asemblyName.FullName
                                , TypeAttributes.Public |
                                TypeAttributes.Class |
                                TypeAttributes.AutoClass |
                                TypeAttributes.AnsiClass |
                                TypeAttributes.BeforeFieldInit |
                                TypeAttributes.AutoLayout
                                , typeof(MyClassParent));
            return typeBuilder;
        }
        private void CreateConstructor(TypeBuilder typeBuilder)
        {
            typeBuilder.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);
        }
        private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
        {
            /* HELP HERE !!!!

                AT THIS PLACE THIS CODE DEFINES A PRIVATE VARIABLE TO SAVE AND SERVE THE PROPERTY DATA
                WHAT I NEED IS TO CHANGE THIS DEFINITION TO THE PARENT METHOD ON MyClassParent ReadProperty AND WriteProperty

            */
            FieldBuilder fieldBuilder = typeBuilder.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

            PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
            MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
            ILGenerator getIl = getPropMthdBldr.GetILGenerator();

            getIl.Emit(OpCodes.Ldarg_0);
            getIl.Emit(OpCodes.Ldfld, fieldBuilder);
            getIl.Emit(OpCodes.Ret);

            MethodBuilder setPropMthdBldr = typeBuilder.DefineMethod("set_" + propertyName,
                  MethodAttributes.Public |
                  MethodAttributes.SpecialName |
                  MethodAttributes.HideBySig,
                  null, new[] { propertyType });

            ILGenerator setIl = setPropMthdBldr.GetILGenerator();
            Label modifyProperty = setIl.DefineLabel();
            Label exitSet = setIl.DefineLabel();

            setIl.MarkLabel(modifyProperty);
            setIl.Emit(OpCodes.Ldarg_0);
            setIl.Emit(OpCodes.Ldarg_1);
            setIl.Emit(OpCodes.Stfld, fieldBuilder);

            setIl.Emit(OpCodes.Nop);
            setIl.MarkLabel(exitSet);
            setIl.Emit(OpCodes.Ret);

            propertyBuilder.SetGetMethod(getPropMthdBldr);
            propertyBuilder.SetSetMethod(setPropMthdBldr);
        }
    }
}

这是如何使用该奇妙的类生成器

    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Reflection;  
    using System.Reflection.Emit;  
    using System.Text;  
    using System.Threading.Tasks;  
    using PropertyBuilderExample;  
      
    namespace PropertyBuilderExample  
    {  
        class Program  
        {  
            static void Main(string[] args)  
            {  
                MyClassBuilder MCB=new MyClassBuilder("Student");  
                var myclass = MCB.CreateObject(new string[3] { "ID", "Name", "Address" }, new Type[3] { typeof(int), typeof(string), typeof(string) });  
               Type TP = myclass.GetType();  
                  
                foreach (PropertyInfo PI in TP.GetProperties())  
                {  
                    Console.WriteLine(PI.Name);  
                }  
                Console.ReadLine();  
            }  
        }  
    }   

2 个答案:

答案 0 :(得分:0)

如何像这样将要生成的代码添加到目标类中

public class MyClassParent : DynamicObject
{
    private dynamic _hostElement;
    public dynamic ReadProperty(string name)
    {
        return _hostElement[name];
    }
    public dynamic WriteProperty(string name, dynamic value)
    {
        return _hostElement[name] = value;
    }

    // EDIT:
    public int ID
    {
        get { return ReadProperty("ID"); }
        set { WriteProperty("ID", value); }
    }
}

并在诸如ILSpy之类的工具中显示结果IL,给出

MyClassParent.get_ID:
IL_0000:  nop         
IL_0001:  ldsfld      UserQuery+MyClassParent+<>o__4.<>p__0
IL_0006:  brfalse.s   IL_000A
IL_0008:  br.s        IL_002E
IL_000A:  ldc.i4.0    
IL_000B:  ldtoken     System.Int32
IL_0010:  call        System.Type.GetTypeFromHandle
IL_0015:  ldtoken     UserQuery.MyClassParent
IL_001A:  call        System.Type.GetTypeFromHandle
IL_001F:  call        Microsoft.CSharp.RuntimeBinder.Binder.Convert
IL_0024:  call        System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite,System.Object,System.Int32>>.Create
IL_0029:  stsfld      UserQuery+MyClassParent+<>o__4.<>p__0
IL_002E:  ldsfld      UserQuery+MyClassParent+<>o__4.<>p__0
IL_0033:  ldfld       System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite,System.Object,System.Int32>>.Target
IL_0038:  ldsfld      UserQuery+MyClassParent+<>o__4.<>p__0
IL_003D:  ldarg.0     
IL_003E:  ldstr       "ID"
IL_0043:  call        UserQuery+MyClassParent.ReadProperty
IL_0048:  callvirt    System.Func<System.Runtime.CompilerServices.CallSite,System.Object,System.Int32>.Invoke
IL_004D:  stloc.0     
IL_004E:  br.s        IL_0050
IL_0050:  ldloc.0     
IL_0051:  ret         

MyClassParent.set_ID:
IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldstr       "ID"
IL_0007:  ldarg.1     
IL_0008:  box         System.Int32
IL_000D:  call        UserQuery+MyClassParent.WriteProperty
IL_0012:  pop         
IL_0013:  ret   

答案 1 :(得分:0)

这是解决方案:

    private void CreateProperty(TypeBuilder typeBuilder, string propertyName, Type propertyType)
    {
        PropertyBuilder propertyBuilder = typeBuilder.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
        MethodBuilder getPropMthdBldr = typeBuilder.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
        ILGenerator getIl = getPropMthdBldr.GetILGenerator();

        string methodName = "ReadProperty" + propertyType.Name;

        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldstr, propertyName);
        getIl.Emit(OpCodes.Call, typeof(MyClassParent).GetMethod("ReadProperty"));
        getIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(getPropMthdBldr);
    }