最近做了一个.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;
}
}
}

  

利用表达式树Expression优化反射性能的更多相关文章

  1. [C#] C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  2. 表达式树 Expression

    转载泛型方法动态生成表达式树 Expression public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> ...

  3. 表达式树(Expression Tree)

    饮水思源 本文并非原创而是下面网址的一个学习笔记 https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/e ...

  4. C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  5. 【C#表达式树 七】 反射在表达式树中的应用 ListInitExpression

    以下都是反射在表达式树中的应用 对象初始化 Expression.MemberInit 反射获取成员(字段 或者属性),绑定数据,然后生成 成员表达式节点 class Animal { public ...

  6. 泛型方法动态生成表达式树 Expression

    public string GetGridJSON(TraderInfo model) { IQueryable<TraderInfo> Temp = db.TraderInfo; if ...

  7. jQuery find() 搜索所有段落中的后代 C# find() 第一个匹配元素 Func 有返回值 Action是没有返回值 Predicate 只有一个参数且返回值为bool 表达式树Expression

    所有p后代span Id为 TotalProject 的 select 标签 的后代 option标签 为选中的 text using System; using System.Collections ...

  8. 表达式树Expression

    Expression表达式树动态查询 在进行数据列表的查询中,我们通常会使用两种方式进行查询: linq查询 数据库sql语句查询 这样固然可以实现查询,本人之前也都是这么做的,因为查询的条件很少.使 ...

  9. C# 表达式树 Expression

    表达式树是定义代码的数据结构. 它们基于编译器用于分析代码和生成已编译输出的相同结构. 几种常见的表达式 BinaryExpression 包含二元运算符的表达式 BinaryExpression b ...

随机推荐

  1. BEC listen and translation exercise 46

    录音文件 https://pan.baidu.com/s/1qYYZGWO The process of learning and exploring a subject can lead to a ...

  2. utc时间、本地时间及时间戳转化

    1.时间戳的概念 时间戳的定义请看百科unix时间戳,需要注意的时间戳为当前时刻减去UTC时间(1970.1.1)零点时刻的秒数差,与当前系统所处的时区无关,同一时刻不管在任何时区下得到的时间戳都是一 ...

  3. codeforces 459D D. Pashmak and Parmida's problem(离散化+线段树或树状数组求逆序对)

    题目链接: D. Pashmak and Parmida's problem time limit per test 3 seconds memory limit per test 256 megab ...

  4. Python TCP通信网络编程

    最近在看廖雪峰老师的基础教程(http://www.liaoxuefeng.com/),今天实现了一下简单Python的Socket的网络编程. 1. Socket网络编程 Socket是网络编程的一 ...

  5. 重写ScrollView实现两个ScrollView的同步滚动显示

    1.背景介绍 最近项目用到两个ScrollView的同步显示,即拖动左边的ScrollView滚动的同时,实现右边的ScrollView同步滚动.此种情形常用在复杂界面布局中,比如左边的ScrollV ...

  6. 机器学习 Generative Learning Algorithm (A)

    引言 前面几讲,我们主要探讨了如何对 p(y|x;θ) (即y 相对于x的条件概率)进行建模的几种学习算法,比如,logistic regression 对 p(y|x;θ) 进行建模的假设函数为 h ...

  7. FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析

    相关博客列表 : FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析 FFMPEG内存操作(二)从内存中读取数及数据格式的转换 FFmpeg内存操作(三)内存转码器 在F ...

  8. ACM学习历程—HDU1695 GCD(容斥原理 || 莫比乌斯)

    Description Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = ...

  9. rman命令详解(三)

    1. Report 命令用户判断数据库的当前可恢复状态和提供数据库备份的特定信息1.1 指定最近没有备份的数据文件查询3天内没有备份过的表空间,可以用如下命令:RMAN> report need ...

  10. [IBM]掌握Ajax,Ajax中的高级请求和响应

    掌握 Ajax, Ajax 中的高级请求和响应 http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro1.html http://www.ibm.c ...