65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2011 年 1 月 12 日

CPOL

1分钟阅读

viewsIcon

20392

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

在之前的文章中,我展示了如何创建表达式树来设置对象的属性。StatCounter

我当时使用的方法不太方便开发者使用。它涉及到显式创建必要的表达式,因为编译器不会生成包含属性或字段设置表达式的表达式树。

最近,有人联系我,希望我帮助开发一种使用开发者友好的 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

唯一的限制是,我们需要能够编写代码来读取属性,才能写入它。

© . All rights reserved.