C#使用表达式树实现对象复制的示例代码

需求背景:对象复制性能优化;同时,在对象复制时,应跳过引用类型的null值复制,值类型支持值类型向可空类型的复制

using Common;
using System;
class Program
{
    static void Main(string[] args)
    {
        TestClassA classA = new TestClassA() { PropA = new TestClass() { Name = "cs1" }, PropB = "c1", PropC = 1 };
        TestClassA classB = new TestClassA() { PropA = new TestClass() { Name = "cs2" }, PropB = "c2", PropC = 2 };
        FastCopy.Copy(classA, classB, false);
        Console.WriteLine(classB.PropA?.Name + ":" + classB.PropB + ":" + classB.PropC);
        TestClassA classC = new TestClassA() { PropA = new TestClass() { Name = "cs1" } };
        TestClassA classD = new TestClassA() { PropA = new TestClass() { Name = "cs2" }, PropB = "c2", PropC = 2 };
        FastCopy.Copy(classC, classD, false);
        Console.WriteLine(classD.PropA?.Name + ":" + classD.PropB + ":" + classD.PropC);
    }
}
public class TestClassA
{
    public TestClass PropA { get; set; }
    public string PropB { get; set; }
    public int? PropC { get; set; }
}
public class TestClass
{
    public string Name { get; set; }
}

输出:

百万次调用耗时:270-300ms

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using static System.Linq.Expressions.Expression;
namespace Common
{
    public static class FastCopy
    {
        static ConcurrentDictionary<string, object> copiers = new ConcurrentDictionary<string, object>();
        /// <summary>
        /// 复制两个对象同名属性值
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
        public static void Copy<S, T>(S source, T target, bool copyNull = true)
        {
            string name = string.Format("{0}_{1}_{2}", typeof(S), typeof(T), copyNull);
            object targetCopier;
            if (!copiers.TryGetValue(name, out targetCopier))
            {
                Action<S, T> copier = CreateCopier<S, T>(copyNull);
                copiers.TryAdd(name, copier);
                targetCopier = copier;
            }
            Action<S, T> action = (Action<S, T>)targetCopier;
            action(source, target);
        }
        /// <summary>
        /// 为指定的两种类型编译生成属性复制委托
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
        /// <returns></returns>
        private static Action<S, T> CreateCopier<S, T>(bool copyNull)
        {
            ParameterExpression source = Parameter(typeof(S));
            ParameterExpression target = Parameter(typeof(T));
            var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
            var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
            // 查找可进行赋值的属性
            var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且
            && (
            sProp.PropertyType == tProp.PropertyType// 属性类型一致 或
            || sProp.PropertyType.IsAssignableFrom(tProp.PropertyType) // 源属性类型 为 目标属性类型 的 子类;eg:object target = string source;   或
            || (tProp.PropertyType.IsValueType && sProp.PropertyType.IsValueType && // 属性为值类型且基础类型一致,但目标属性为可空类型 eg:int? num = int num;
            ((tProp.PropertyType.GenericTypeArguments.Length > 0 ? tProp.PropertyType.GenericTypeArguments[0] : tProp.PropertyType) == sProp.PropertyType))
            )).Count() > 0);
            List<Expression> expressionList = new List<Expression>();
            foreach (var prop in copyProps)
            {
                if (prop.PropertyType.IsValueType)// 属性为值类型
                {
                    PropertyInfo sProp = typeof(S).GetProperty(prop.Name);
                    PropertyInfo tProp = typeof(T).GetProperty(prop.Name);
                    if (sProp.PropertyType == tProp.PropertyType)// 属性类型一致 eg:int num = int num;    或   int? num = int? num;
                    {
                        var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));
                        expressionList.Add(assign);
                    }
                    else if (sProp.PropertyType.GenericTypeArguments.Length <= 0 && tProp.PropertyType.GenericTypeArguments.Length > 0)// 属性类型不一致且目标属性类型为可空类型 eg:int? num = int num;
                    {
                        var convert = Convert(Expression.Property(source, prop.Name), tProp.PropertyType);
                        var cvAssign = Assign(Expression.Property(target, prop.Name), convert);
                        expressionList.Add(cvAssign);
                    }
                }
                else// 属性为引用类型
                {
                    var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));// 编译生成属性赋值语句   target.{PropertyName} = source.{PropertyName};
                    var sourcePropIsNull = Equal(Constant(null, prop.PropertyType), Property(source, prop.Name));// 判断源属性值是否为Null;编译生成  source.{PropertyName} == null
                    var setNull = IsTrue(Constant(copyNull));// 判断是否复制Null值 编译生成  copyNull == True
                    var setNullTest = IfThen(setNull, assign);
                    var condition = IfThenElse(sourcePropIsNull, setNullTest, assign);
                    /**
                     * 编译生成
                     * if(source.{PropertyName} == null)
                     * {
                     *   if(setNull)
                     *   {
                     *     target.{PropertyName} = source.{PropertyName};
                     *   }
                     * }
                     * else
                     * {
                     *   target.{PropertyName} = source.{PropertyName};
                     * }
                     */
                    expressionList.Add(condition);
                }
            }
            var block = Block(expressionList.ToArray());
            Expression<Action<S, T>> lambda = Lambda<Action<S, T>>(block, source, target);
            return lambda.Compile();
        }
    }
}

如果完整复制,去掉逻辑判断,同时可通过泛型类,不在使用字典,性能还可以提升。

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Common
{
    public static class FastCopy<S, T>
    {
        static Action<S, T> action = CreateCopier();
        /// <summary>
        /// 复制两个对象同名属性值
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">源对象</param>
        /// <param name="target">目标对象</param>
        /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
        public static void Copy(S source, T target, bool copyNull = true)
        {
            action(source, target);
        }
        /// <summary>
        /// 为指定的两种类型编译生成属性复制委托
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="copyNull">源对象属性值为null时,是否将值复制给目标对象</param>
        /// <returns></returns>
        private static Action<S, T> CreateCopier()
        {
            ParameterExpression source = Expression.Parameter(typeof(S));
            ParameterExpression target = Expression.Parameter(typeof(T));
            var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList();
            var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList();
            // 查找可进行赋值的属性
            var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且
            && (
            sProp.PropertyType == tProp.PropertyType// 属性类型一致
            )).Count() > 0);
            var block = Expression.Block(from p in copyProps select Expression.Assign(Expression.Property(target, p.Name), Expression.Property(source, p.Name)));
            Expression<Action<S, T>> lambda = Expression.Lambda<Action<S, T>>(block, source, target);
            return lambda.Compile();
        }
    }
}

百万次耗时:100ms左右

到此这篇关于C#使用表达式树实现对象复制的示例代码的文章就介绍到这了,更多相关C#表达式树对象复制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C#表达式树Expression动态创建表达式

    上一篇中说到了 Expression 的一些概念性东西,其实也是为了这一篇做知识准备.为了实现 EFCore 的多条件.连表查询,简化查询代码编写,也就有了这篇文章. 在一些管理后台中,对数据进行多条件查询是一件很普遍的事情,比如在用户列表需要实现可以对 "用户名"."手机号"."账户是否冻结" 等等一系列的条件查询,常见的处理方式就是通过一系列 if...else... 来对条件进行拼接.这会导致查询接口实现起来堆叠了一堆看起来有用但实际很繁琐

  • C# 快速高效率复制对象(表达式树)

    1.需求 在代码中经常会遇到需要把对象复制一遍,或者把属性名相同的值复制一遍. 比如: public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } public class StudentSecond { public int Id { get; set; } public string Name { get; set; } p

  • C# 使用反射来实现对象的深度复制方法

    实现方式 通过挨个罗列的方式一次复制子对象是非常耗费人力的,如果子对象是引用类型,则还要需要考虑是否对子对象进一步深拷贝. 实际应用中,一个类如果有几十个子对象,挨个复制对于开发人员来说索然无味比较费时费力. 所以使用反射机制来实现.   但是如果是服务端运行的话,还是建议手动的实现. 毕竟反射机制比直接写出来的效率要慢一些. 代码: public static class DeepCopyHelper { public static object Copy(this object obj) {

  • C#使用表达式树实现对象复制的示例代码

    需求背景:对象复制性能优化:同时,在对象复制时,应跳过引用类型的null值复制,值类型支持值类型向可空类型的复制 using Common; using System; class Program { static void Main(string[] args) { TestClassA classA = new TestClassA() { PropA = new TestClass() { Name = "cs1" }, PropB = "c1", PropC

  • Opencv对象追踪的示例代码

    1 HSV上下限 颜色的HSV上下限如下表: 2 追踪单个颜色 import cv2 as cv import numpy as np cap = cv.VideoCapture(0) lower_color = np.array([0, 43, 46]) upper_color = np.array([10, 255, 255]) while cap.isOpened(): # 读取帧 _, frame = cap.read() # 转换颜色空间 BGR 到 HSV hsv = cv.cvtC

  • 利用OpenCV进行对象跟踪的示例代码

    目录 OpenCV 对象跟踪 OpenCV 对象跟踪器 物体跟踪 总结 OpenCV 对象跟踪 这篇文章使用 OpenCV 中内置的八种不同的对象跟踪算法,实现对物体的跟踪. 首先,介绍一下8种跟踪算法. 然后,演示如何使用OpenCV实现这些跟踪算法. 最后,对本文做总结. OpenCV 对象跟踪器 OpenCV 八种对象跟踪器: BOOSTING Tracker:基于用于驱动 Haar 级联 (AdaBoost) 背后的机器学习的相同算法,但与 Haar 级联一样,已有十多年的历史.这个跟踪

  • js中如何把字符串转化为对象、数组示例代码

    例如 复制代码 代码如下: var test='{ colkey: "col", colsinfo: "NameList" }' 很明显是一个对象,但如何把文本转为对象呢.使用eval();注意一定要加括号,否则会转换失败 把文本转化为对象 复制代码 代码如下: var test='{ colkey: "col", colsinfo: "NameList" }' var obj2=eval("("+test

  • js自动生成对象的属性示例代码

    例如 我们有如下这么一个对象 复制代码 代码如下: var obj = { a:{ b:"bb" } } 但是我们现在想给 obj 对象增加如下属性 obj.a.b.c.d.f="ff" ; 我们一般会如下做,obj.a.b.c={},obj.a.b.c.d={} ,obj.a.b.c.d.f="ff" ;但是如果我的属性很多,这样的方法时不可行的.现在提供一个自动生成对象属性的方法 复制代码 代码如下: function autoCreateO

  • Java中树的存储结构实现示例代码

    一.树 树与线性表.栈.队列等线性结构不同,树是一种非线性结构. 一棵树只有一个根节点,如果一棵树有了多个根节点,那它已经不再是一棵树了,而是多棵树的集合,也被称为森林. 二.树的父节点表示法 树中除根节点之外每个节点都有一个父节点,为了记录树中节点与节点之间的父子关系,可以为每个节点增加一个parent域,用以记录该节点的父节点. package com.ietree.basic.datastructure.tree; import java.util.ArrayList; import ja

  • C# 表达式树Expression Trees的知识梳理

    目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达式树 调试 简介 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等. 你可以对表达式树中的代码进行编辑和运算.这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性. 一.

  • C#表达式树基础教程

    什么是表达式树 来自微软官方文档的定义: 表达式树以树形数据结构表示代码. 它能干什么呢? 你可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 好不好玩? 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET Framework 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL). 哪里有应用? ORM框架.工作流框架等,使用到 Lambda 的代码...动

  • C#表达式树的基本用法讲解

    表达式树使用一种类似树的结构来表示代码,它的每个节点都是一个表达式,比如方法调用和x<y这样的二元运算等.我们可以对表达式树的内容进行编辑和运算,这样能够动态修改可执行代码,以及动态创建查询等.我们可以使用匿名lambda表达式或者C# API来创建表达式树. 这一系列文章,主要是对C#表达式树的一种总结,基本知识参考MSDN的内容 这部分内容可以直接到MSDN上查看,后面的几篇文章主要分享一下,在工作中碰到的应用到表达式树的部分,谨做为记录和分享. 生成表达式树 通过lambda表达式创建表达

随机推荐