12ExpressionTree应用场景一

1、AutoMapper自动映射框架:用于不同类之间的相互转换,原理就是通过表达式树来实现的。

不同类相互转换有多种方式,比如反射,拿到一个类里的所有属性再循环复制给另一个类。或者是通过序列化和反序列化将连个 类互相转换,不过效率都没有通过表达式树效率高。

我们有时候需要进行对象转换:

我们定义如下两个类:

public class Student
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
        }


        public class Person
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int Age { get; set; }
        }

那么我们如果有逻辑需要将学生类转为person类的话,最直接的方法就是声明好将学生类中对应的成员的值赋值给person类

Student student = new Student()
            {
                Id = 1,
                Name = "张三",
                Age = 18
            };


            Person newStudent = new Person()
            {
                Id = student.Id,
                Name = student.Name,
                Age = student.Age
            };

可以看出字段少的话没啥问题,字段多就会很繁琐。

创建自动对象转换器

首先我们创建一个ExpressionMapper泛型类,需要两个泛型类型,TIn, TOut,TIn代表要被转换的类, TOut代表转换为该类

public class ExpressionMapper<TIn, TOut>
    {
        //表达式树自动创建对象转换器委托
        private static Func<TIn, TOut> func = null;

        //通过类名.MyMapper方法时候会自动执行该构造函数
        static ExpressionMapper()
        {

            //创建参数表达式
            ParameterExpression parameterExp = Expression.Parameter(typeof(TIn), "p");

            //创建一个类的成员绑定的集合
            List<MemberBinding> memberBindingList = new List<MemberBinding>();

            //通过反射获取TOut类的所有属性并且遍历
            foreach (var item in typeof(TOut).GetProperties())
            {

                //得到成员表达式,相当于新属性
                MemberExpression member = Expression.Property(parameterExp, typeof(TIn).GetProperty(item.Name));

                //进行属性绑定,相当于属性初始化,给新属性赋值,表示新属性的值对应的旧属性的值
                MemberBinding memberBinding = Expression.Bind(item, member);//item:要转为的类的属性,member:被转换的类的属性
                //添加到类成员属性绑定集合
                memberBindingList.Add(memberBinding);

            }


            //通过反射获取TOut类的所有自动并且遍历
            foreach (var item in typeof(TOut).GetFields())
            {

                //得到成员表达式,相当于新属性
                MemberExpression member = Expression.Field(parameterExp, typeof(TIn).GetField(item.Name));

                //进行属性绑定,相当于属性初始化,给新属性赋值
                MemberBinding memberBinding = Expression.Bind(item, member);

                memberBindingList.Add(memberBinding);

            }

            //创建新对象并初始化,生成成员表达式
            MemberInitExpression memberInitExpression =Expression.MemberInit(Expression.New(typeof(TOut)), memberBindingList.ToArray());

            //生成表达式
            Expression<Func<TIn, TOut>> expression = Expression.Lambda<Func<TIn, TOut>>(memberInitExpression, new ParameterExpression[] { parameterExp });


            //编译,然后将表达式赋值给一个委托
            func = expression.Compile();

        }

        //调用该委托的方法
        public static TOut MyMapper(TIn t)
        {
            //调用这个类的委托即可
            return func(t);
        }

    }

该类中几个Expression节点的图解
1、循环用反射取对应属性,并且创建成员节点
image

2、循环里创建成员绑定节点,处理类中成员绑定关系
image

3、使用MemberInitExpression生成新对象节点,并将MemberBinding集合里的属性绑定到新对象中
image

4、自动生成的委托如下:
image

我们可以进行测试 :发现,调用该工具即可帮我们进行对象转换并且给属性赋值了
image

posted @ 2021-11-13 21:43  青仙  阅读(61)  评论(0编辑  收藏  举报