【问题标题】:Create an Action<T> to "set" a property, when I am provided with the LINQ Expression for the "get"当我获得“get”的 LINQ 表达式时,创建一个 Action<T> 来“设置”一个属性
【发布时间】:2011-01-04 17:19:21
【问题描述】:

如果 lambda 表达式为属性提供“get”方法,我希望能够生成一个已编译的表达式来设置属性。

这就是我要找的东西:

public Action<int> CreateSetter<T>(Expression<Func<T, int>> getter)
{
    // returns a compiled action using the details of the getter expression tree, or null
    // if the write property is not defined.
}

我仍在尝试了解各种类型的 Expression 类,所以如果您能指出正确的方向,那就太好了。

【问题讨论】:

  • 你不是说Action&lt;T, int&gt;吗?此外,我们也可以将int 设为类型参数。
  • Ani,是的,你是对的 - 我的意思是 Action 和 int 当然也是一个通用参数。

标签: c# expression-trees


【解决方案1】:

以@Ani 的回答为起点,您可以使用以下内容生成编译表达式

[TestMethod]
public void CreateSetterFromGetter()
{
    Action<Person, int> ageSetter = InitializeSet((Person p) => p.Age);
    Action<Person, string> nameSetter = InitializeSet((Person p) => p.Name);

    Person p1 = new Person();
    ageSetter(p1, 29);
    nameSetter(p1, "John");

    Assert.IsTrue(p1.Name == "John");
    Assert.IsTrue(p1.Age == 29);
}

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

public static Action<TContainer, TProperty> InitializeSet<TContainer, TProperty>(Expression<Func<TContainer, TProperty>> getter)
{
    PropertyInfo propertyInfo = (getter.Body as MemberExpression).Member as PropertyInfo;

    ParameterExpression instance = Expression.Parameter(typeof(TContainer), "instance");
    ParameterExpression parameter = Expression.Parameter(typeof(TProperty), "param");

    return Expression.Lambda<Action<TContainer, TProperty>>(
        Expression.Call(instance, propertyInfo.GetSetMethod(), parameter),
        new ParameterExpression[] { instance, parameter }).Compile();
}

您应该缓存已编译的表达式以方便多次使用。

【讨论】:

  • 完全正确,但我不确定表达式是这里的最佳路线。但是这个例子是一个很好的例子(用于说明)。
  • 我将此标记为答案,因为它使用 Expression 类作为解决方案,这正是我所寻找的。谢谢亚当
【解决方案2】:

您当然可以遍历表达式树,然后使用Delegate.CreateDelegate 创建适当的Action&lt;,&gt;。这很简单,除了所有的验证检查(我不确定我是否已经涵盖了所有内容):

我不是表达式树专家,但我不认为构建表达式树然后在这里调用 Compile 是可能的,因为表达式树不能包含据我所知,赋值语句。编辑:显然,这些已添加到 .NET 4 中。这是一个很难找到的功能,因为 C# 编译器似乎没有能够从 lambda 构建它们)。

public static Action<TContaining, TProperty>
    CreateSetter<TContaining, TProperty>
    (Expression<Func<TContaining, TProperty>> getter)
{
    if (getter == null)
        throw new ArgumentNullException("getter");

    var memberEx = getter.Body as MemberExpression;

    if (memberEx == null)
        throw new ArgumentException("Body is not a member-expression.");

    var property = memberEx.Member as PropertyInfo;

    if (property == null)
        throw new ArgumentException("Member is not a property.");

    if(!property.CanWrite)
        throw new ArgumentException("Property is not writable.");

    return (Action<TContaining, TProperty>)
           Delegate.CreateDelegate(typeof(Action<TContaining, TProperty>),
                                   property.GetSetMethod());
}

用法

public class Person { public int Age { get; set; } }

...

static void Main(string[] args)
{
    var setter = CreateSetter((Person p) => p.Age);
    var person = new Person();
    setter(person, 25);

    Console.WriteLine(person.Age); // 25     
}

请注意,这会创建一个 open 实例委托,这意味着它不会绑定到 TContaining 的任何特定实例。将其修改为绑定到 特定的 实例很简单;您还必须将TContaining 传递给该方法,然后使用Delegate.CreateDelegate 的不同重载。该方法的签名将类似于:

public static Action<TProperty> CreateSetter<TContaining, TProperty>
        (Expression<Func<TContaining, TProperty>> getter, TContaining obj)

【讨论】:

  • 不确定,但我认为他们在 .net 4 中添加了分配。但是 setter 应该已经有正确的签名,所以没有必要。
  • 是的,分配是在 4.0 - 但你真的不需要它; Delegate.CreateDelegate 是理想的。 +1
  • @CodeInChaos、@Marc Gravell:谢谢,这对我来说是个新闻。
  • 很好的答案。谢谢。 @Marc,为什么 Delegate.CreateDelegate 比 Expression.Lambda 更好?
  • @Ani “显然,这些已添加到 .NET 4 中。这是一个很难找到的功能,因为 C# 编译器似乎无法从 lambda 构建它们)。” int x; new Expression&lt;Action&gt;(() =&gt; x = 5); - 我相信这是从 lambda 构建的分配,但我还没有真正检查过。
【解决方案3】:

恐怕只有指针(我不在电脑旁)-但是;

  • lambda 的 .Body 很可能是 MemberExpression
  • 做一个安全的演员(as等)并访问.Member
  • 既然你相信这是一个属性,这应该是一个 PropertyInfo,所以测试/转换等
  • 从一个PropertyInfo,调用GetSetMethod()获取对应的MethodInfo
  • 使用 Delegate.CreateDelegate 将其作为委托获取(传递操作类型)
  • 最后,将返回的委托转换为预期的委托类型

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-09
    • 2012-09-28
    • 1970-01-01
    • 1970-01-01
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多