如何获得Expression <func <object>&gt;目标对象设置值?</func <object>

时间:2014-06-13 13:19:30

标签: c# lambda expression func

我尝试创建一个函数来比较原始值和更新值,并将原始设置为更新的(如果不同)。该功能做得更多,所以我简化为专注于主题:

public void Match<T>(Expression<Func<object>> original, Expression<Func<object>> updated)
{
    var mex = original.Body as MemberExpression;

    var funcOriginal = original.Compile();
    var funcUpdated = updated.Compile();

    var valueOriginal = funcOriginal();
    var valueUpdated = funcUpdated();

    if (valueOriginal != valueUpdated)
    {
       var info = mex.Member as PropertyInfo;
       var target = ???; //How to get the original.TestProperty here?

       info.SetValue(target, valueUpdated);
    }
}

我想这样打电话:

manager.Match<TestClass>(() => original.TestProperty, () => updated.TestProperty);

2 个答案:

答案 0 :(得分:4)

您的成员信息的Expression属性表示从中获取属性的变量。您只需调用它来将其评估为值。

public static void Match<T>(Expression<Func<T>> original, 
    Expression<Func<T>> updated)
{
    var mex = original.Body as MemberExpression;
    var valueOriginal = original.Compile()();
    var valueUpdated = updated.Compile()();

    if (!object.Equals(valueOriginal, valueUpdated))
    {
        var info = mex.Member as PropertyInfo;

        var target = Expression.Lambda(mex.Expression).Compile().DynamicInvoke();

        info.SetValue(target, valueUpdated);
    }
}

当然,如果这个类型是一个值类型,那么你将通过调用这个表达式创建的是该值的副本,你最终会改变副本,但只要它是一个引用类型你将要复制引用,实际上对象将会发生变异。

你可以采取另一种截然不同的途径。您可以简单地创建一个表达式来表示您对该表达式赋予的值,而不是尝试评估可以将此属性赋值给的变量。如果从值类型访问属性,此代码甚至可以工作,因为它实际上是在改变变量,而不是获取该变量的值并将其变异。

public static void Match<T>(Expression<Func<T>> original, 
    Expression<Func<T>> updated)
{
    var mex = original.Body as MemberExpression;
    var valueOriginal = original.Compile()();
    var valueUpdated = updated.Compile()();

    if (!object.Equals(valueOriginal, valueUpdated))
    {
        var body = Expression.Assign(
            Expression.MakeMemberAccess(mex.Expression, mex.Member),
            updated.Body);

        Expression.Lambda<Action>(body).Compile().Invoke();
    }
}

尽管使方法具有通用性,但您实际上并没有将通用参数用于两个函数的结果类型。

答案 1 :(得分:0)

如果您想要类型安全,可以传递原始

public void Match<TSource, TType>(TSource dest, Expression<Func<TSource, TType>> original, TType updateValue)
{
    var originalValue = original.Compile()(dest);
    if (!updateValue.Equals(originalValue))
    {
        // get prop name from expression
        var prop = original.GetMemberInfo().Name;
        typeof(TSource).GetProperty(prop).SetValue(dest, updateValue);
    }
}

//helper class to get propinfo from expression
public static class ExpressionExtensions
{
    public static MemberInfo GetMemberInfo(this Expression expression)
    {
        var lambda = (LambdaExpression) expression;

        MemberExpression memberExpression;
        if (lambda.Body is UnaryExpression)
        {
            var unaryExpression = (UnaryExpression) lambda.Body;
            memberExpression = (MemberExpression) unaryExpression.Operand;
        }
        else
            memberExpression = (MemberExpression) lambda.Body;

        return memberExpression.Member;
    }
}

一样使用它
manager.Match(original, o=>o.TestProperty, updated.TestProperty);

我没有对它进行测试,但是应该可以工作或者让你接近。