• 表达式树对性能的影响


           至于表达式树是啥我就不想说了,大家可以参考脑袋的相关文章。一至以来,我虽然接触过表达式树,还写过一篇表达式树和泛型委托相关的文章,但终究只现在表面功能,并没有真正认识到它的好处。这两天我看了脑袋的相关文章,每篇都给我们留下了一些练习题,有一道是关于一次性修改实体集中相关属性的问题。

          原题:日常应用中我们常常会和一些数据实体类打交道。现在假设我们的数据实体类里面定义了许多public的属性。请用Expression Tree实现一个动态逻辑,可以更新任意一种数据实体类的集合,比如List<Book>, List<Employee>等等中每一个元素的指定属性。比如我要对一个集合中所有Book元素的Author属性进行更新,您的程序就可以这样调用:

        List<Book> books = …

        SetAllProperty(books, “Author”, “Ninputer”);

        当然您写的方法不知道Book类型,也不知道我要更新哪个属性,它应该能动态地支持任意的实体类集合。

        首先我想到的就是遍历实体的属性,找对就修改的属性,然后调用SetValue方式就OK,代码也不多,如下: 

    public List<T> SetAllProperty(List<T> t, object propertyName, object propertyValue)
             {
                 
    if (null == t||null ==propertyName ||null ==propertyValue )
                 {
                     
    return null ;
                 }
                 
    for (int i = 0; i < t.Count; i++)
                 {
                     PropertyInfo[] proAs 
    = t[i].GetType().GetProperties();
                     
    foreach (PropertyInfo proA in proAs)
                     {
                         
    if (!proA.CanRead &&!proA .CanWrite ) continue;

                         
    if (proA.Name.ToLower()==propertyName .ToString ().ToLower ())
                         {
                             proA.SetValue(t[i], propertyValue , 
    null);
                         }
                     
                     }
                 }
                 
    return t;
             }

        
          问题:上面的代码理论上和实际操作上都能够很好的完成练习题,但有个性能问题,如果这个实体集特别大的时候,比如一百万或者更多,此时就会特别耗时。于时我在脑袋的指点下,花了大半天时间终于有所结果:

          版本一:结果都是正确的,问题时,这种写法比遍历属性赋值方式更慢。问题就在于LambdaExpression lambda = Expression.Lambda(call, parameter, value);var exp = lambda.Compile();这两条语句上,这个表达式经过Compile()后会生成一个Delegate,Compile方法在内部使用了Emit,而问题就出了DynamicInvoke上面,DynamicInvoke方法其本质与反射调用差不多。

    public List<T> ExpressionTree<T>(List<T> collection, object propertyName, string   propertyValue)
            {
                ParameterExpression parameter 
    = Expression.Parameter(typeof(T), "x");

                ParameterExpression value 
    = Expression.Parameter(typeof(string  ), "propertyValue");

                MethodInfo setter 
    = typeof(T).GetMethod("set_"+propertyName );

                MethodCallExpression call 
    = Expression.Call(parameter, setter, value);

                LambdaExpression lambda 
    = Expression.Lambda(call, parameter, value);
                var exp 
    = lambda.Compile();
                
    for (int i = 0; i < collection.Count; i++)
                {
                    exp.DynamicInvoke(collection[i], propertyValue);

                }
                
    return collection;
            }

          
          版本二:生成强类型委托。强类型委托改掉了DynamicInvoke带来的性能问题。

                    说明:强类型委托我知道的有两种方式:

                         第一:Func<(T, TResult> 泛型委托,当然这种委托还包含一个参数以上的方式,但它需要有返回值。
                         第二:Action<T, 参数类型>,与Func不同的就是它并不需要返回值。所以下面我采用了Action方式。

    public List<T> ExpressionTree<T>(List<T> collection, object propertyName, string propertyValue)
            {
                ParameterExpression parameter 
    = Expression.Parameter(typeof(T), "x");

                ParameterExpression value 
    = Expression.Parameter(typeof(string), "propertyValue");

                MethodInfo setter 
    = typeof(T).GetMethod("set_" + propertyName);

                MethodCallExpression call 
    = Expression.Call(parameter, setter, value);
                var lambda 
    = Expression.Lambda<Action<T, string>>(call, parameter, value);
                var exp 
    = lambda.Compile();
                
    for (int i = 0; i < collection.Count; i++)
                {
                    exp(collection[i], propertyValue);
                }
                
    return collection;
            }

           
           版本二和反射方式的性能比较:先帖下相关代码
            1:实体类:  

    public class Book
        {
            
    public string sTitle
            { 
    getset; }
            
    public string sContent
            { 
    getset; }
            
    public int iID
            { 
    getset; }
        }

          
           2:修改任务泛型实体集属性类:上面都有,就不重复了。
           3:构造一个包含一百万的实体集:    

    private List<Book> GetData()
            {
                List
    <Book> list = new List<Book>();
                
    for (int i = 0; i < 1000000; i++)
                {
                    Book b 
    = new Book();
                    b.sTitle 
    = "标题" + i.ToString();
                    b.sContent 
    = "内容" + i.ToString();
                    b.iID 
    = i;
                    list.Add(b);
                }
                
    return list;
            }

          
           4:性能比较:
                

    List<Book> list = this.GetData();
                SetAllPropertyMethod
    <Book> s = new SetAllPropertyMethod<Book>();         
                Stopwatch sw 
    = new Stopwatch();
                sw.Start();
                list 
    = s.SetAllProperty(list, "sTitle""1");
                sw.Stop();
                
    double time1 = sw.ElapsedMilliseconds;

                Response.Write(
    "反射方式用时:"+time1 .ToString ());

                sw 
    = new Stopwatch();
                sw.Start();
                list 
    = s.ExpressionTree<Book>(list, "sTitle""1");
                sw.Stop();
                time1 
    = sw.ElapsedMilliseconds;
                Response.Write(
    "表达式树用时:" + time1.ToString());

          
           5:输出结果如下:反射方式用时:3198表达式树用时:38 ,可能看出接近100倍的性能优势。

                说话这个Stopwatch原来我也没有用过,汗,编码快四年了,居然这种好东西没用过。是不是有些朋友也和我一要只会利用TimeSpan来比较呢,哈哈。
                说明:这种表达式树只生成一次,即可完成整个实体集的应用,起到了非常好的复用作用。

           表达式树的优势:
               表达式树拥有语义清晰,强类型等优势,表达式树的计算对于性能的影响会越来越大,由于减少了编译操作和反射操作的次数,计算所需开销大大降低。

  • 相关阅读:
    Linux操作系统基础(完结)
    单台主机上DB2 10.5和arcgis 10.4 空间数据库配置
    java连接oracle数据库使用SERVICE NAME、SID以及TNSName不同写法
    arcgis desktop 地理编码服务发布
    利用arcgis发布综合又详细的地理定位服务
    centos 安装arcgis server 10.1
    spark源代码
    bootstrap.memory_lock: true导致Elasticsearch启动失败问题
    安装jdk后出现bash: ./java: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录
    pyspark采用python3开发
  • 原文地址:https://www.cnblogs.com/ASPNET2008/p/1575198.html
Copyright © 2020-2023  润新知