zoukankan      html  css  js  c++  java
  • 利用表达式树Expression优化反射性能

    最近做了一个.Net Core环境下,基于NPOI的Excel导入导出以及Word操作的服务封装,
    涉及到大量反射操作,在性能优化过程中使用到了表达式树,记录一下。

    Excel导入是相对比较麻烦的一块,实现的效果是:调用方只需要定义一个类,只需要标记特性,
    服务读取Excel=>校验(正则、必填、整数范围、日期、数据库是否存在、数据重复) =>将校验结果返回 => 提供方法将Excel数据
    转换为指定类集合。

    在最后一步转换,最开始用反射实现,性能较差;后来通过了反射+委托,表达式树方式进行优化,
    最终性能接近了硬编码。见图,转换近5000条有效数据,耗时仅100毫秒不到,是反射的近20倍。

    读取Excel数据之后,我将数据读取到了自定义的两个类(方便后面的校验)

       public class ExcelDataRow
        {
            /// <summary>
            /// 行号
            /// </summary>
            public int RowIndex { get; set; }
    
            /// <summary>
            /// 单元格数据
            /// </summary>
            public List<ExcelDataCol> DataCols { get; set; } = new List<ExcelDataCol>();
    
            /// <summary>
            /// 是否有效
            /// </summary>
            public bool IsValid { get; set; }
    
            /// <summary>
            /// 错误信息
            /// </summary>
            public string ErrorMsg { get; set; }
        }
    
        public class ExcelDataCol : ExcelCol
        {
            /// <summary>
            /// 对应属性名称
            /// </summary>
            public string PropertyName { get; set; }
    
            /// <summary>
            /// 行号
            /// </summary>
            public int RowIndex { get; set; }
    
            /// <summary>
            /// 字符串值
            /// </summary>
            public string ColValue { get; set; }
        }
    

    校验完之后,需要将ExcelDataRow转换为指定类型

    using System;
    using System.Collections;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Ade.OfficeService.Excel
    {
        /// <summary>
        /// 生成表达式目录树 缓存
        /// </summary>
        public class ExpressionMapper
        {
            private static Hashtable Table = Hashtable.Synchronized(new Hashtable(1024));
    
            /// <summary>
            /// 将ExcelDataRow快速转换为指定类型
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="dataRow"></param>
            /// <returns></returns>
            public static T FastConvert<T>(ExcelDataRow dataRow)
            {
                //利用表达式树,动态生成委托并缓存,得到接近于硬编码的性能
                //最终生成的代码近似于(假设T为Person类)
                //Func<ExcelDataRow,Person>
                //      new Person(){
                //          Name = Convert(ChangeType(dataRow.DataCols.SingleOrDefault(c=>c.PropertyName == prop.Name).ColValue,prop.PropertyType),prop.ProertyType),
                //          Age = Convert(ChangeType(dataRow.DataCols.SingleOrDefault(c=>c.PropertyName == prop.Name).ColValue,prop.PropertyType),prop.ProertyType)
                //      }
                // }
    
                string propertyNames = string.Empty;
                dataRow.DataCols.ForEach(c => propertyNames += c.PropertyName + "_");
                var key = typeof(T).FullName + "_" + propertyNames.Trim('_');
    
    
                if (!Table.ContainsKey(key))
                {
                    List<MemberBinding> memberBindingList = new List<MemberBinding>();
    
                    MethodInfo singleOrDefaultMethod = typeof(Enumerable)
                                                                .GetMethods()
                                                                .Single(m => m.Name == "SingleOrDefault" && m.GetParameters().Count() == 2)
                                                                .MakeGenericMethod(new[] { typeof(ExcelDataCol) });
    
                    foreach (var prop in typeof(T).GetProperties())
                    {
                        Expression<Func<ExcelDataCol, bool>> lambdaExpr = c => c.PropertyName == prop.Name;
    
                        MethodInfo changeTypeMethod = typeof(ExpressionMapper).GetMethods().Where(m => m.Name == "ChangeType").First();
    
                        Expression expr =
                            Expression.Convert(
                                Expression.Call(changeTypeMethod
                                    , Expression.Property(
                                        Expression.Call(
                                              singleOrDefaultMethod
                                            , Expression.Constant(dataRow.DataCols)
                                            , lambdaExpr)
                                            , typeof(ExcelDataCol), "ColValue"), Expression.Constant(prop.PropertyType))
                                        , prop.PropertyType);
    
                        memberBindingList.Add(Expression.Bind(prop, expr));
                    }
    
                    MemberInitExpression memberInitExpression = Expression.MemberInit(Expression.New(typeof(T)), memberBindingList.ToArray());
                    Expression<Func<ExcelDataRow, T>> lambda = Expression.Lambda<Func<ExcelDataRow, T>>(memberInitExpression, new ParameterExpression[]
                    {
                        Expression.Parameter(typeof(ExcelDataRow), "p")
                    });
    
                    Func<ExcelDataRow, T> func = lambda.Compile();//拼装是一次性的
                    Table[key] = func;
                }
                var ss = (Func<ExcelDataRow, T>)Table[key];
    
                return ((Func<ExcelDataRow, T>)Table[key]).Invoke(dataRow);
            }
    
            public static object ChangeType(string stringValue, Type type)
            {
                object obj = null;
    
                Type nullableType = Nullable.GetUnderlyingType(type);
                if (nullableType != null)
                {
                    if (stringValue == null)
                    {
                        obj = null;
                    }
    
                }
                else if (typeof(System.Enum).IsAssignableFrom(type))
                {
                    obj = Enum.Parse(type, stringValue);
                }
                else
                {
                    obj = Convert.ChangeType(stringValue, type);
                }
    
                return obj;
            }
        }
    }
    

      

  • 相关阅读:
    33.数组声明方式(var构造函数) 、检测数组类型、数组的属性(封装好的就一个length)、数组的方法
    31.this指向(写出调用链,找最近对象) this的默认绑定 隐式绑定 显示绑定(call(绑定对象) apply(绑定对象) 当括号内没放绑定对象的时候恢复默认绑定) bind
    31.
    30.函数作用域链 (GO AO 也叫词法作用域链)、 调用栈、调用栈涉及this绑定
    29.包装类(构造函数) 包装类作用及调用栈
    916. Word Subsets
    246. Strobogrammatic Number
    445. Add Two Numbers II
    2. Add Two Numbers
    341. Flatten Nested List Iterator
  • 原文地址:https://www.cnblogs.com/holdengong/p/9948218.html
Copyright © 2011-2022 走看看