C#使用表达式树(LambdaExpression)动态更新类的属性值(示例代码)

有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种场景的个性化操作,如动态拼接查询条件、排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树。

本文给大家分享C#使用表达式树(LambdaExpression)动态更新类的属性值的相关知识,在某些业务中会遇到需要同步两个类的属性值的情况,而且有些字段是要过滤掉的。如果手动赋值则需要写很多重复的代码:

 public class Teacher
        {
            public Guid Id { get; set; }
            public string Name { get; set; }
            public string Age { get; set; }
        }
        public class Student
        {
            public Guid Id { get; set; }
            public string Name { get; set; }
            public string Age { get; set; }
        }
   /// <summary>
        /// 比如需要把teacher的某些属性值赋给student,而id不需要赋值
        /// </summary>
        /// <param name="student"></param>
        /// <param name="teacher"></param>
        public static void SetProperty(Student student, Teacher teacher)
        {
            if (student.Name != teacher.Name)
            {
                student.Name = teacher.Name;
            }
            if (student.Age != teacher.Age)
            {
                student.Age = teacher.Age;
            }
        }

使用反射的话性能考虑,尝试写一个扩展方法使用lambda表达式树去构建一个方法

public static class ObjectExtensions
    {
        /// <summary>
        /// 缓存表达式
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TTarget"></typeparam>
        public static class MapperAccessor<TSource, TTarget>
        {
            private static Action<TSource, TTarget, string[]> func { get; set; }
            public static TSource Set(TSource source, TTarget target, params string[] properties)
            {
                if (func == null)
                {
                    var sourceType = typeof(TSource);
                    var targetType = typeof(TTarget);
                    BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public;
                    if (properties.Length == 0)
                    {
                        //get all properties
                        if (sourceType == targetType)
                        {
                            //如果是相同类型则获取所有属性
                            properties = sourceType.GetProperties(bindingFlags).Select(x => x.Name)
                                .ToArray();
                        }
                        else
                        {
                            //如果没有传指定的属性则默认获取同名属性
                            List<PropertyInfo> propertyInfos = new List<PropertyInfo>();
                            foreach (var property in sourceType.GetProperties(bindingFlags))
                            {//不同类型指定同名且类型相同的属性
                                var targetProperty = targetType.GetProperty(property.Name, bindingFlags);
                                if (targetProperty != null && targetProperty.PropertyType == property.PropertyType)
                                {
                                    propertyInfos.Add(property);
                                }
                            }
                            properties = propertyInfos.Select(x => x.Name).ToArray();
                        }
                    }
                    //定义lambda 3个参数
                    var s = Expression.Parameter(typeof(TSource), "s");
                    var t = Expression.Parameter(typeof(TTarget), "t");
                    var ps = Expression.Parameter(typeof(string[]), "ps");
                    //获取泛型扩展方法Contains
                    var methodInfo = typeof(Enumerable).GetMethods().FirstOrDefault(e => e.Name == "Contains" && e.GetParameters().Length == 2);
                    if (methodInfo == null)
                    {
                        // properties.Contains()
                        throw new NullReferenceException(nameof(methodInfo));
                    }
                    MethodInfo genericMethod = methodInfo.MakeGenericMethod(typeof(String));//创建泛型方法
                    List<BlockExpression> bs = new List<BlockExpression>();
                    foreach (string field in properties)
                    {
                        //获取两个类型里面的属性
                        var sourceField = Expression.Property(s, field);
                        var targetField = Expression.Property(t, field);
                        //创建一个条件表达式
                        var notEqual = Expression.NotEqual(sourceField, targetField);//sourceField!=targetField
                        var method = Expression.Call(null, genericMethod, ps, Expression.Constant(field));//ps.Contains(f);
                        //构建赋值语句
                        var ifTrue = Expression.Assign(sourceField, targetField);
                        //拼接表达式 sourceField!=targetField&&ps.Contains(f)
                        var condition = Expression.And(notEqual, Expression.IsTrue(method));
                        //判断是否相同,如果不相同则赋值
                        var expression = Expression.IfThen(condition, ifTrue);
                        bs.Add(Expression.Block(expression));
                    }
                    var lambda = Expression.Lambda<Action<TSource, TTarget, string[]>>(Expression.Block(bs), s, t, ps);
                    func = lambda.Compile();
                }
                func.Invoke(source, target, properties);
                return source;
            }
        }
        /// <summary>
        /// 通过目标类更新源类同名属性值
        /// </summary>
        /// <typeparam name="TSource">待更新的数据类型</typeparam>
        /// <typeparam name="TTarget">目标数据类型</typeparam>
        /// <param name="source">源数据</param>
        /// <param name="target">目标数据</param>
        /// <param name="properties">要变更的属性名称</param>
        /// <returns>返回源数据,更新后的</returns>
        public static TSource SetProperties<TSource, TTarget>(this TSource source, TTarget target, params string[] properties)
        {
            return MapperAccessor<TSource, TTarget>.Set(source, target, properties);
        }
    }

编写测试方法

 /// <summary>
        /// 比如需要把teacher的某些属性值赋给student,而id不需要赋值
        /// </summary>
        /// <param name="student"></param>
        /// <param name="teacher"></param>
        public static void SetProperty(Student student, Teacher teacher)
        {
            if (student.Name != teacher.Name)
            {
                student.Name = teacher.Name;
            }
            if (student.Age != teacher.Age)
            {
                student.Age = teacher.Age;
            }
        }
        public static void SetProperty2(Student student, Teacher teacher, params string[] properties)
        {
            var sourceType = student.GetType();
            var targetType = teacher.GetType();
            foreach (var property in properties)
            {
                var aP = sourceType.GetProperty(property);
                var bP = targetType.GetProperty(property);
                var apValue = aP.GetValue(student);
                var bpValue = bP.GetValue(teacher);
                if (apValue != bpValue)
                {
                    aP.SetValue(student, bpValue);
                }
            }
        }

        static (List<Student>, List<Teacher>) CreateData(int length)
        {
            var rd = new Random();
            (List<Student>, List<Teacher>) ret;
            ret.Item1 = new List<Student>();
            ret.Item2 = new List<Teacher>();
            for (int i = 0; i < length; i++)
            {
                Student student = new Student()
                {
                    Id = Guid.NewGuid(),
                    Name = Guid.NewGuid().ToString("N"),
                    Age = rd.Next(1, 100)
                };
                ret.Item1.Add(student);
                Teacher teacher = new Teacher()
                {
                    Id = Guid.NewGuid(),
                    Name = Guid.NewGuid().ToString("N"),
                    Age = rd.Next(1, 100)
                };
                ret.Item2.Add(teacher);
            }
            return ret;
        }
        static void Main(string[] args)
        {

            var length = 1000000;
            var data = CreateData(length);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < length; i++)
            {
                SetProperty(data.Item1[i], data.Item2[i]);
            }
            sw.Stop();
            Console.WriteLine($"手写方法耗时:{sw.ElapsedMilliseconds}ms");
            data.Item1.Clear();
            data.Item2.Clear();
            var data2 = CreateData(length);
            sw.Restart();
            for (int i = 0; i < length; i++)
            {
                data2.Item1[i].SetProperties(data2.Item2[i], nameof(Student.Age), nameof(Student.Name));
            }
            data2.Item1.Clear();
            data2.Item2.Clear();
            sw.Stop();
            Console.WriteLine($"lambda耗时:{sw.ElapsedMilliseconds}ms");
            var data3 = CreateData(length);
            sw.Restart();
            for (int i = 0; i < length; i++)
            {
                SetProperty2(data3.Item1[i], data3.Item2[i], nameof(Student.Age), nameof(Student.Name));
            }
            sw.Stop();
            Console.WriteLine($"反射耗时:{sw.ElapsedMilliseconds}ms");
            data3.Item1.Clear();
            data3.Item2.Clear();

            Console.ReadKey();
        }

可以看到性能和手写方法之间的差距,如果要求比较高还是手写方法,如果字段多的话写起来是很痛苦的事。但是日常用这个足够了,而且是扩展方法,通用性很强。

到此这篇关于C#使用表达式树(LambdaExpression)动态更新类的属性值的文章就介绍到这了,更多相关C#表达式树类的属性值内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

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

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

  • C#表达式树Expression基础讲解

    什么是表达式树 表达式树以树形数据结构表示代码,其中每一个节点都是一种表达式,比如方法调用和 x < y 这样的二元运算等.可以对表达式树中的代码进行编辑和运算. 这样能够动态修改可执行代码.在不同数据库中执行 LINQ 查询以及创建动态查询. 表达式树还能用于动态语言运行时 (DLR) 以提供动态语言和 .NET 之间的互操作性,同时保证编译器编写员能够发射表达式树而非 Microsoft 中间语言 (MSIL). 这段话是来自官网( [表达式树 (C#) | Microsoft Docs](

  • 浅谈c#表达式树Expression简单类型比较demo

    实例如下: using System; using System.Linq.Expressions; class DynamicPredicate { public static Expression<Func<T, T, bool>> Generate<T>(string op) { ParameterExpression x = Expression.Parameter(typeof(T), "x"); ParameterExpression y

  • C#使用表达式树(LambdaExpression)动态更新类的属性值(示例代码)

    有看过我之前发表过的C#相关文章分享和阅读过我代码的朋友们可能会在我的代码里面经常看到各种各样的λ表达式动态拼接,C#的λ表达式树是一个好东西,也是别的语言学不来的,熟悉掌握λ表达式就能够实现各种场景的个性化操作,如动态拼接查询条件.排序方式等,也能够实现替代反射的高性能操作,比如我们常用到的IQueryable和IEnumerable,每个扩展方法就全是λ表达式树. 本文给大家分享C#使用表达式树(LambdaExpression)动态更新类的属性值的相关知识,在某些业务中会遇到需要同步两个类

  • C#用表达式树构建动态查询的方法

    前文介绍了C#中表达式树的基本知识,在实际中,表达式树有很多用法,这里举几个例子,说明如何使用表达式树构建动态查询,从而扩展LINQ的查询方法. 在LINQ中,只要数据源实现了IQuerable<T>接口,表达式树就可以用来表示结构化查询.比如,LINQ提供了用来查询关系数据源的IQueryable<T>接口的实现,C#编译器在执行这类数据源查询时,会在运行时生成表达式树,然后,查询会遍历表达式树的数据结构,然后将其转换成针对特定数据源的合适的查询语言. 下面的几个例子演示了如何使

  • Python实现动态添加类的属性或成员函数的解决方法

    某些时候我们需要让类动态的添加属性或方法,比如我们在做插件时就可以采用这种方法.用一个配置文件指定需要加载的模块,可以根据业务扩展任意加入需要的模块. 本文就此简述了Python实现动态添加类的属性或成员函数的解决方法,具体方法如下: 首先我们可以参考ulipad的实现:mixin. 这里做的比较简单,只是声明一个类,类初始化的时候读取配置文件,根据配置列表加载特定目录下的模块下的函数,函数和模块同名,将此函数动态加载为类的成员函数. 代码如下所示: class WinBAS(Bas): def

  • Mybatis实现动态增删改查功能的示例代码

    一.Mybatis 流程简介 最近在看 Mybatis 的源码,大致了解整个框架流程后便手写了一个特别简单的SimpMybatis的小Demo,来巩固这整个框架的学习.下图是我所画的框架大致执行流程:

  • android 仿QQ动态背景、视频背景的示例代码

    本文介绍了android 仿QQ动态背景.视频背景的示例代码,分享给大家,具体如下: 效果如下: 如上图类似效果图: 1, 自定义视频类 继承VideoView public class CustomVideoView extends VideoView { public CustomVideoView(Context context) { super(context); } public CustomVideoView(Context context, AttributeSet attrs)

  • SpringBoot如何实现一个实时更新的进度条的示例代码

    前言 博主近期接到一个任务,大概内容是:导入excel表格批量修改状态,期间如果发生错误则所有数据不成功,为了防止重复提交,做一个类似进度条的东东. 那么下面我会结合实际业务对这个功能进行分析和记录. 正文 思路 前端使用bootstrap,后端使用SpringBoot分布式到注册中心,原先的想法是导入表格后异步调用修改数据状态的方法,然后每次计算修改的进度然后存放在session中,前台jquery写定时任务访问获取session中的进度,更新进度条进度和百分比.但是这存在session在服务

  • Vue+ElementUI实现从后台动态填充下拉框的示例代码

    1.首先编写前端代码,将elementUI中的标签写到.vue界面中.  <el-select       v-model="xxxQuery.xxxid"       placeholder="请在下拉框中选择名称"       maxlength="255"       :disabled="false"       clearable>             <el-option          

  • Java利用File类创建文件的示例代码

    只需要调用该类的一个方法createNewFile(),但是在实际操作中需要注意一些事项,如判断文件是否存在,以及如何向新建文件中写入数据等. import java.io.*; public class CreateNewFile{ //该方法用于创建文件,参数分别是文件路径和文件名.文件内容,如:myfile.doc HelloJava! public void createNewFile(String fileDirectoryAndName,String fileContent){ tr

  • Python实现动态二维码生成的示例代码

    目录 第一个库:MyQR 普通的二维码制作 带背景图片的二维码制作 带动图背景的二维码制作 第二个库:qrcode 简单的二维码制作 带背景图片的二维码制作 大家好,我是辰哥~ 今天给大家分享两个制作二维码的Python库,可以生成普通的二维码.图片背景版二维码.动图GIF版二维. 第一个库:MyQR 安装 pip install MyQR 普通的二维码制作 from MyQR import myqr #运行 myqr.run('http://mtw.so/5w0Ubz') 二维码附上了链接,用

随机推荐