ps:问: 反射有3种方式,一个是获取值,一个是赋值,一个是调用方法 (如构造器 静态方法 普通方法等),哪个才是性能元凶
先总结: 表达式树 就是代码的拼接, 所以有以下三个区域
以下是例子的由简单到复杂的汇总,代码在后面的框里面
以下所有的表达式树都带你从入参 到代码块 到返回值 到Compile()的步骤来拼接
//入参
ParameterExpression t1 = Expression.Parameter(typeof(Int32), "t1");
ParameterExpression t2 = Expression.Parameter(typeof(Int32), "t2");
//代码块
BinaryExpression multiply = Expression.Multiply(t1, t2);
//返回值Block
BlockExpression block = Expression.Block(multiply);
//编译
Expression<Func<int, int, int>> expression = Expression.Lambda<Func<int, int, int>>(block, t1, t2);
Func<int, int, int> func = expression.Compile();
上面说了 表达式树不允许出现显示的代码块 不允许出现return,所以注意以下错误的写法
Expression<Func<int, int,int>> expression = (m,n)=> m * n;
//以下是错误形式
Expression<Action<int, int>> expression = (m,n)=> { };
Expression<Action<int, int>> expression = (m,n)=> {m + n };
Expression<Func<int, int,int>> expression = (m,n)=> { return m + n; };
注意: 没有复杂逻辑块,如 {},不允许显示写 return,大家可以试试上面注释code
public class T01
{
public String Name { get; set; }
}
public class T02
{
public String Name { get; set; }
}
1 操作 Expression<Func<T01, String>> func = (T01 m) => m.Name;
//如果想 得到 Fuc<Test, int> t = Test test => test.M
//入参
ParameterExpression m = Expression.Parameter(typeof(T01), "m");
//代码块
MemberExpression left = Expression.Property(m, typeof(T01).GetProperty(nameof(T01.Name)));
//错误的写法是
//Expression.Property(null, typeof(T01).GetProperty(nameof(T01.Name))),
//因为有些以为 我都 传了typeof(Test) 肯定获取到对应属性的值,
//其实表达式树是 编译代码的,所以每一步都得实现,少一个不可。
//这里的 是 m.Name 其实分为3步 m . Name,都必须有
//返回值
BlockExpression block1 = Expression.Block(left);
Expression<Func<T01, String>> expression = Expression.Lambda<Func<T01, String>>(block1, m);
Func<T01, String> func = expression.Compile();
2 操作属性赋值: Expression<Action<T01>> func = (T01 m) => m.Name = "new name";
//入参
ParameterExpression m = Expression.Parameter(typeof(T01), "m");
//代码块
//拼接 m.Name
MemberExpression left = Expression.Property(m, typeof(T01).GetProperty(nameof(T01.Name)));
// m.Name = "new name"
BinaryExpression assignExpression1 = Expression.Assign(left, Expression.Constant("new name", typeof(String)));
//返回值 此处无
BlockExpression block = Expression.Block(assignExpression1);
Expression<Action<T01>> expression = Expression.Lambda<Action<T01>>(block, m);
Action<T01> func = expression.Compile();
T01 t01 = new T01() { Name = "old name" };
func(t01);
Console.WriteLine(t01.Name);//new name
3 操作属性赋值: Expression<Action<T01,String>> func = (T01 m, String str) => m.Name = str;
//入参
ParameterExpression m = Expression.Parameter(typeof(T01), "m");
ParameterExpression str = Expression.Parameter(typeof(String), "str");
//代码块
//拼接 m.Name
MemberExpression left = Expression.Property(m, typeof(T01).GetProperty(nameof(T01.Name)));
// m.Name = str
//Assign 赋值的
BinaryExpression assignExpression1 = Expression.Assign(left, left);
//返回值
BlockExpression block = Expression.Block(assignExpression1);
Expression<Action<T01, String>> expression = Expression.Lambda<Action<T01, String>>(block, m, str);
Action<T01, String> func = expression.Compile();
T01 t01 = new T01() { Name = "old name" };
func(t01,"new name");
Console.WriteLine(t01.Name);//new name
4 操作属性赋值:Expression<Func<String, T01>> expression1 = (String str)=>new T01() { Name = str };
这里有个问题是 new T01(){};
4.1. 这里是对象初始化模块,而不是 var t01 = new T02();t02.Name="";
所以一定得区分,而对象初始化模块 用得 Bind() 关系处理的,需要和Assign()区分开来
4.2. 注意Expression.MemberInit(Expression.New(typeof(T01))) == Expression.New(typeof(T01)), 如果没有绑定关系 其实可以去掉 MemberInit()的写法,就和 可以去掉所有的Block一样,以及 Expression.Property()只传递一个 name 值一样,其实里面做了简化
//入参
ParameterExpression str = Expression.Parameter(typeof(String), "str");
//代码块
MemberAssignment bind = Expression.Bind(typeof(T01).GetProperty(nameof(T01.Name)), str);
//返回值
MemberInitExpression newT01 = Expression.MemberInit(Expression.New(typeof(T01)), bind);
BlockExpression block = Expression.Block(newT01);
Expression<Func<String, T01>> expression = Expression.Lambda<Func<String, T01>>(block,str);
Func<String, T01> func = expression.Compile();
T01 t01 = new T01() { Name = "old name" };
t01 = func("new name");
Console.WriteLine(t01.Name);//new name
5 操作 Expression<Action<T01,T02>> action = (T01 t1, T02 t2)=> t2.Name = t1.Name;
//入参
ParameterExpression t1 = Expression.Parameter(typeof(T01), "t1");
ParameterExpression t2 = Expression.Parameter(typeof(T02), "t2");
//代码块
//第二步,t2.Name 拆解为 参数t2, 属性符号 . 属性名称 Name
var member1 = Expression.Property(t2, nameof(T02.Name));
//拆解 t1.Name
var member2 = Expression.Property(t1, nameof(T01.Name));
//将member2赋值给member1
var member = Expression.Assign(member1, member2);
Expression block = Expression.Block(member);
Expression<Action<T01, T02>> expression = Expression.Lambda<Action<T01, T02>>(block, t1, t2);
var func = expression.Compile();
T01 t01 = new T01() { Name = "This is t1 name" };
T02 t02 = new T02() { Name = "This is t2 name" };
func(t01, t02);
Console.WriteLine(t02.Name); //输出的是 This is t1 name;
6 写一个Fun<T01, T02> func = T01 t1 => new T02(){ Name = t1.Name}的转换器
//定义入参
ParameterExpression t1 = Expression.Parameter(typeof(T01), "t1");
//代码块
//拆解 t1.Name
var member2 = Expression.Property(t1, nameof(T01.Name));
//因为这里是new,所以 这里实际是 绑定 T02.Name与 T01.Name的关系
var member = Expression.Bind(typeof(T02).GetProperty(nameof(T02.Name)), member2);
//当创建对象的时候,依据绑定关系 new T02()
//当然也可以 var member = Expression.Bind(typeof(T02).GetProperty(nameof(T02.Name)), Expression.Constant("xxxxx"));
//如果没有 member 那么可以直接 New() 不需要MemberInit()
Expression block = Expression.Block(Expression.MemberInit(Expression.New(typeof(T02)), member));
Expression<Func<T01, T02>> expression = Expression.Lambda<Func<T01, T02>>(block, t1);
var func = expression.Compile();
T01 t01 = new T01() { Name = "This is t1 name" };
T02 t02 = new T02() { Name = "This is t2 name" };
t02 = func(t01);
Console.WriteLine(t02.Name);//这里输出的 "This is t1 name"
注意 这里是 Bind() 而不是 Assign(), 那这里可以用 Assign()吗?
答案是不行.因为 Assign 的写法就是 T02 t02 = new T02(); t02.Name = t01.Name;return t02; 这个原因上面解释过, 这里在强调下, 表达式树 不允许代码块,不允许return,只允许单操作.
最后 在强调下,表达式树 其实 就是 入参 出参 返回的代码拼接,然后执行成 我们想要的代码,虽然看起来复杂,但是实际就是一步步来的.
留个作业,
public class MapHelper
{
private static Dictionary<String, Object> dicFunc = new Dictionary<string, Object>();
/// 没有判断泛型 IsGenericType
/// 没操作 属性 不同类型 转不同类型的操作(重新调用Map类即可), ps => IEnumerable 转换需要调用 Expression.Call( IEnumerable.Select(Map) )来处理转换条件
/// 没操作 IsNullable 转为值类型数据转换 需要 Expression.PropertyType("value")来强制性赋值到 基础类型
/// 没操作 特性转换
/// 可以理解成只做 类型相同字段 的 值赋值转换
public static void Map<T1, T2>(T1 source, T2 destination) where T1 : class, new() where T2 : class, new()
{
Type tIn = typeof(T1);
Type tResult = typeof(T2);
String key = $"{tIn}_Convert_{tResult}";
if (!dicFunc.TryGetValue(key, out var func))
{
lock (dicFunc)
{
if (!dicFunc.TryGetValue(key, out func))
{
ParameterExpression t1 = Expression.Parameter(tIn);
ParameterExpression t2 = Expression.Parameter(tResult);
List<Expression> list = new List<Expression>();
Dictionary<PropertyInfo, PropertyInfo> map = new Dictionary<PropertyInfo, PropertyInfo>();
foreach (var item in tResult.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite && x.CanRead))
{
String name = item.Name;
var p1 = tIn.GetProperty(name);
if (p1 == null) continue;
if (!p1.PropertyType.IsPublic || !p1.CanWrite || !p1.CanRead || p1.PropertyType != item.PropertyType) continue;
MemberExpression mIn = Expression.Property(t1, p1);
MemberExpression mResult = Expression.Property(t2, item);
var memberBinding = Expression.Assign(mResult, mIn);
list.Add(memberBinding);
}
Expression<Action<T1, T2>> lambda = Expression.Lambda<Action<T1, T2>>(Expression.Block(list), t1, t2);
var action = lambda.Compile();
dicFunc[key] = action;
func = action;
}
}
}
((Action<T1, T2>)func)(source, destination);
}
public static TResult Map<TIn, TResult>(TIn source) where TIn : class, new() where TResult : class, new()
{
Type tIn = typeof(TIn);
Type tResult = typeof(TResult);
String key = $"{tIn}_New_{tResult}";
if (!dicFunc.TryGetValue(key, out var func))
{
lock (dicFunc)
{
if (!dicFunc.TryGetValue(key, out func))
{
ParameterExpression t1 = Expression.Parameter(tIn);
List<MemberBinding> list = new List<MemberBinding>();
foreach (var item in tResult.GetProperties().Where(x => x.PropertyType.IsPublic && x.CanWrite && x.CanRead))
{
String name = item.Name;
var p1 = tIn.GetProperty(name);
if (p1 == null) continue;
if (!p1.PropertyType.IsPublic || !p1.CanWrite || !p1.CanRead || p1.PropertyType != item.PropertyType) continue;
MemberExpression property1 = Expression.Property(t1, p1);
var memberBinding = Expression.Bind(item, property1);
list.Add(memberBinding);
}
Expression<Func<TIn, TResult>> lambda;
if (list.Count == 0)
{
lambda = Expression.Lambda<Func<TIn, TResult>>(Expression.Block(Expression.Constant(null, tResult)), t1);
}
else
{
lambda = Expression.Lambda<Func<TIn, TResult>>(Expression.MemberInit(Expression.New(tResult), list), t1);
}
var action = lambda.Compile();
dicFunc[key] = action;
func = action;
}
}
}
return ((Func<TIn, TResult>)func)(source);
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。