• 玩转动态编译


    • 实例属性的读取

    先来回顾下静态属性读取的IL代码:

    .method public hidebysig instance string AAA() cil managed
    {
        .maxstack 8
        L_0000: call string blqw.IL.Demo.Program/MyClass::get_Name()
        L_0005: ret 
    } 
    string AAA()
    {
        return MyClass.Name;
    }
    C#代码

    再来看下读取实例属性的IL代码

    .method private hidebysig instance string AAA(class blqw.IL.Demo.Program/MyClass my) cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: callvirt instance string blqw.IL.Demo.Program/MyClass::get_Name()
        L_0006: ret 
    }
    string AAA(MyClass my)
    {
        return my.Name;
    }
    C#代码

    区别很明显,多个一个指令ldarg.0 ,并且指令有所区别

    //     将索引为 0 的参数加载到计算堆栈上。
    public static readonly OpCode Ldarg_0;

    操作实例方法和操作静态方法不同,静态方法不需要任何额外的参数,而实例方法必须要提供一个参数,这个参数指示操作的实例对象

    转换成C#代码就是这样的

    public static Func<MyClass, string> ILTest()
    {
        var type = typeof(MyClass);
        var prop = type.GetProperty("Name");//反射属性
    
        var dm = new DynamicMethod("", typeof(string), new[] { typeof(MyClass) }, type);
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Callvirt, prop.GetGetMethod());
        il.Emit(OpCodes.Ret);
        return (Func<MyClass, string>)dm.CreateDelegate(typeof(Func<MyClass, string>));
    }

    • 实例属性的设置

    IL代码

    .method private hidebysig instance void AAA(class blqw.IL.Demo.Program/MyClass my, string name) cil managed
    {
        .maxstack 8
        L_0000: ldarg.0 
        L_0001: ldarg.1 
        L_0002: callvirt instance void blqw.IL.Demo.Program/MyClass::set_Name(string)
        L_0007: ret 
    }

    好吧,又多了一个参数,不过这个是显而易见的,既然你要设置值,总要把值当作参数传递进去吧

    对应C#代码如下:

    public static Action<MyClass, string> ILTest()
    {
        var type = typeof(MyClass);
        var prop = type.GetProperty("Name");//反射属性
    
        var dm = new DynamicMethod("", null, new[] { typeof(MyClass), typeof(string) }, type);
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        il.Emit(OpCodes.Callvirt, prop.GetSetMethod());
        il.Emit(OpCodes.Ret);
        return (Action<MyClass, string>)dm.CreateDelegate(typeof(Action<MyClass, string>));
    }

    重复来重复去都是这几样东西了..有些无聊了吧...

     那么接下来就做一些实际情况下的应用了

    • 实际应用

    这次我要举起来的栗子就是DataTable转模型对象

    不过在这之前,我要对现有的方法进行一些调整

    public delegate void PropertySetter(object instance, object value);
    public static PropertySetter CreateSetter(PropertyInfo property)
    {
        var type = property.DeclaringType;
        var dm = new DynamicMethod("", null, new[] { typeof(object), typeof(object) }, type);
        //=== IL ===
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Ldarg_1);
        if (property.PropertyType.IsValueType)//判断属性类型是否是值类型
        {
            il.Emit(OpCodes.Unbox,property.PropertyType);//如果是值类型就拆箱
        }
        else
        {
            il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
        }
        il.Emit(OpCodes.Callvirt, property.GetSetMethod());
        il.Emit(OpCodes.Ret);
        //=== IL ===
        return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));
    }

    修改的地方不是很多,应该不难理解,关于类型转换部分,请参考上一篇

    现在我可以很方便的通过这个方法创建一个任意实例属性的Set方法委托

    接下来我需要一个的新的类

    public class ObjectProperty
    {
        public PropertyInfo Info { get; set; }
        public PropertySetter Setter { get; set; }
    }

    这个类包含一个属性和这个属性的Set方法委托

    在接下来我需要一个方法,把任意一个类中的所有公开的实例属性,转换成ObjectProperty集合

    static readonly Dictionary<Type, ObjectProperty[]> Cache = new Dictionary<Type, ObjectProperty[]>();
    
    public static ObjectProperty[] GetProperties(Type type)
    {
        ObjectProperty[] arr;
        if (Cache.TryGetValue(type, out arr))//优先从缓存中获取
        {
            return arr;
        }
        PropertyInfo[] ps = type.GetProperties(); 
        arr = new ObjectProperty[ps.Length];
        for (int i = 0; i < ps.Length; i++)
        {
            ObjectProperty op = new ObjectProperty();
            op.Info = ps[i];
            op.Setter = CreateSetter(op.Info);  //之前定义的方法
            arr[i] = op;
        }
        Cache.Add(type, arr); //加入缓存
        return arr;
    }

     把他们整合起来

    public class ObjectProperty
    {
        /// <summary> 属性信息
        /// </summary>
        public PropertyInfo Info { get; set; }
        /// <summary> Set方法委托
        /// </summary>
        public PropertySetter Setter { get; set; }
        //缓存
        static readonly Dictionary<Type, ObjectProperty[]> Cache = new Dictionary<Type, ObjectProperty[]>();
    
        /// <summary> 获取一个类中的所有公开实例属性和它们的Set方法委托
        /// </summary>
        public static ObjectProperty[] GetProperties(Type type)
        {
            ObjectProperty[] arr;
            if (Cache.TryGetValue(type, out arr))//优先从缓存中获取
            {
                return arr;
            }
            PropertyInfo[] ps = type.GetProperties();
            arr = new ObjectProperty[ps.Length];
            for (int i = 0; i < ps.Length; i++)
            {
                ObjectProperty op = new ObjectProperty();
                op.Info = ps[i];
                op.Setter = CreateSetter(op.Info);  //之前定义的方法
                arr[i] = op;
            }
            Cache.Add(type, arr); //加入缓存
            return arr;
        }
        /// <summary> 创建指定属性的Set方法委托
        /// </summary>
        public static PropertySetter CreateSetter(PropertyInfo property)
        {
            var type = property.DeclaringType;
            var dm = new DynamicMethod("", null, new[] { typeof(object), typeof(object) }, type);
            //=== IL ===
            var il = dm.GetILGenerator();
            il.Emit(OpCodes.Ldarg_0);
            il.Emit(OpCodes.Ldarg_1);
            if (property.PropertyType.IsValueType)//判断属性类型是否是值类型
            {
                il.Emit(OpCodes.Unbox, property.PropertyType);//如果是值类型就拆箱
            }
            else
            {
                il.Emit(OpCodes.Castclass, property.PropertyType);//否则强转
            }
            il.Emit(OpCodes.Callvirt, property.GetSetMethod());
            il.Emit(OpCodes.Ret);
            //=== IL ===
            return (PropertySetter)dm.CreateDelegate(typeof(PropertySetter));
        }
    }
    C#代码

    现在就可以写出一个将DataTable转为实体类的方法了

    public static List<T> ConvertToModels<T>(DataSet ds)
        where T : new()
    {
        var prop = ObjectProperty.GetProperties(typeof(T));
        List<T> list = new List<T>(ds.Tables[0].Rows.Count);
        var cols = ds.Tables[0].Columns;
        foreach (DataRow row in ds.Tables[0].Rows)
        {
            T m = new T();
            foreach (var p in prop)
            {
                if (cols.Contains(p.Info.Name))
                {
                    var val = row[p.Info.Name];
                    if ((val is DBNull) == false)
                    {
                        p.Setter(m, val);
                    }
                }
            }
            list.Add(m);
        }
        return list;
    }

    好了,我现在模拟出一个DataSet和一个实体类来测试下

    public class User
    {
        public User()
        {
    
        }
        public int Id { get; set; }
        public string Name { get; set; }
        public bool Sex { get; set; }
        public Guid Uid { get; set; }
        public DateTime Time { get; set; }
        public string SexText
        {
            get
            {
                return Sex ? "" : "";
            }
            set
            {
                Sex = (value == "");
            }
        }
    }
    //模拟方法
    static public DataSet GetDataSet(string sql)
    {
        DataTable table = new DataTable("User");
        table.Columns.Add("Id", typeof(int));
        table.Columns.Add("Name", typeof(string));
        table.Columns.Add("Sex", typeof(bool));
        table.Columns.Add("Uid", typeof(Guid));
        table.Columns.Add("Time", typeof(DateTime));
        table.Columns.Add("多出来的属性", typeof(string));
        for (int i = 0; i < 20; i++)
        {
            table.Rows.Add(i, "blqw" + i, true, Guid.NewGuid(), DateTime.Now, "多余的");
        }
    
        DataSet ds = new DataSet();
        ds.Tables.Add(table);
        return ds;
    }
    C#代码

    • 反射和IL

    就功能上来说IL可以做的,反射都可以做.基本上IL的操作指令很多参数都是需要用到反射对象的

    那么我们为什么要选择麻烦的IL,而不直接用反射呢,答案就是性能

    就拿上面的栗子来说,如果我们用反射来实现的话是这样的

    static public List<T> ConvertToModels2<T>(DataSet ds)
        where T : new()
    {
        var prop = ObjectProperty.GetProperties(typeof(T));
        List<T> list = new List<T>(ds.Tables[0].Rows.Count);
        var cols = ds.Tables[0].Columns;
        foreach (DataRow row in ds.Tables[0].Rows)
        {
            T m = new T();
            foreach (var p in prop)
            {
                if (cols.Contains(p.Info.Name))
                {
                    var val = row[p.Info.Name];
                    if (Convert.IsDBNull(val) == false)
                    {
                        p.Info.SetValue(m, val, null);//这里直接用反射的SetValue
                    }
                }
            }
            list.Add(m);
        }
        return list;
    }
    反射代码

    这只是构造10000个只有5个属性的实体类而已

    因为先运行的是动态编译IL的测试,所以缓存什么的都已经在这个时候建好了,下面反射只是调用缓存

    如果这个测试还看不出太大区别的话,那就看下直接对比Set部分的性能

    一般来说是6倍左右的性能差异

  • 相关阅读:
    Linux(Ubuntu)安装Mujoco、mujoco-py的详细步骤、安装教程
    Model-Free Episodic Control
    Efficient Off-Policy Meta-Reinforcement Learning via Probabilistic Context Variables
    On First-Order Meta-Learning Algorithms
    Meta-Q-Learning
    Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks
    Meta Learning via Learned Loss
    Python不同版本切换
    Ubuntu下压缩解压文件
    Python写入CSV文件的问题
  • 原文地址:https://www.cnblogs.com/blqw/p/3261240.html
Copyright © 2020-2023  润新知