• 利用表达式树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;
            }
        }
    }
    

      

  • 相关阅读:
    [BZOJ4755][JSOI2016]扭动的回文串(manacher+Hash)
    十二省联考2019部分题解
    [BZOJ2959]长跑(LCT+并查集)
    [BZOJ4541][HNOI2016]矿区(平面图转对偶图)
    笛卡尔树
    [CF896C]Willem, Chtholly and Seniorious(珂朵莉树)
    [BZOJ4349]最小树形图
    [BZOJ1858][SCOI2010]序列操作(线段树)
    [PA2014]Parking
    [PA2014]Budowa
  • 原文地址:https://www.cnblogs.com/holdengong/p/9948218.html
Copyright © 2020-2023  润新知