以开发友好的方式创建属性设置表达式树





5.00/5 (1投票)
如何以开发友好的方式创建属性设置表达式树。
在之前的文章中,我展示了如何创建表达式树来设置对象的属性。
我当时使用的方法不太方便开发者使用。它涉及到显式创建必要的表达式,因为编译器不会生成包含属性或字段设置表达式的表达式树。
最近,有人联系我,希望我帮助开发一种使用开发者友好的 lambda 表达式来生成属性设置表达式树的命令模式框架。
简单来说,给定这个实体类
public class Person
{
public string Name { get; set; }
}
相关人员希望编写如下代码
var et = Set((Person p) => p.Name = "me");
其中 et
是代表属性赋值的表达式树。
所以,如果不能这样做,让我们尝试下一个最佳方案,即将检索属性信息与检索要分配给属性的值分开
var et = Set((Person p) => p.Name, () => "me");
这是编译器可以处理的。
Set
的实现接收一个表达式来检索属性信息,以及另一个表达式来检索要分配给属性的值
public static Expression<Action<TEntity>> Set<TEntity, TValue>(
Expression<Func<TEntity, TValue>> propertyGetExpression,
Expression<Func<TValue>> valueExpression)
此方法的实现从属性 get 表达式 (propertyGetExpression
) 的主体和值表达式 (valueExpression
) 中获取属性信息,以构建一个赋值表达式,并使用与属性 get
表达式相同的参数作为其参数来构建 lambda 表达式
public static Expression<Action<TEntity>> Set<TEntity, TValue>(
Expression<Func<TEntity, TValue>> propertyGetExpression,
Expression<Func<TValue>> valueExpression)
{
var entityParameterExpression = (ParameterExpression)
(((MemberExpression)(propertyGetExpression.Body)).Expression);
return Expression.Lambda<Action<TEntity>>(
Expression.Assign(propertyGetExpression.Body, valueExpression.Body),
entityParameterExpression);
}
现在,我们可以使用该表达式转换为另一个上下文,或者直接编译并使用它
var et = Set((Person p) => p.Name, () => name);
Console.WriteLine(person.Name); // Prints: p => (p.Name = “me”)
var d = et.Compile();
d(person);
Console.WriteLine(person.Name); // Prints: me
它甚至可以支持闭包
var et = Set((Person p) => p.Name, () => name);
Console.WriteLine(person.Name); // Prints: p => (p.Name = value(<>c__DisplayClass0).name)
var d = et.Compile();
name = "me";
d(person);
Console.WriteLine(person.Name); // Prints: me
name = "you";
d(person);
Console.WriteLine(person.Name); // Prints: you
对于预期的场景来说,不太有用(但仍然可行)的是构建一个接收要分配给属性的值作为参数的表达式树
public static Expression<Action<TEntity, TValue>> Set<TEntity,
TValue>(Expression<Func<TEntity, TValue>> propertyGetExpression)
{
var entityParameterExpression =
(ParameterExpression)(((MemberExpression)(propertyGetExpression.Body)).Expression);
var valueParameterExpression = Expression.Parameter(typeof(TValue));
return Expression.Lambda<Action<TEntity, TValue>>(
Expression.Assign(propertyGetExpression.Body, valueParameterExpression),
entityParameterExpression,
valueParameterExpression);
}
这个新的表达式可以这样使用
var et = Set((Person p) => p.Name);
Console.WriteLine(person.Name); // Prints: (p, Param_0) => (p.Name = Param_0)
var d = et.Compile();
d(person, "me");
Console.WriteLine(person.Name); // Prints: me
d(person, "you");
Console.WriteLine(person.Name); // Prints: you
唯一的限制是,我们需要能够编写代码来读取属性,才能写入它。