【问题标题】:How to get Expression<Func<object>> target object to set value?如何让 Expression<Func<object>> 目标对象设置值?
【发布时间】:2014-06-13 13:19:29
【问题描述】:

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

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);

【问题讨论】:

    标签: c# lambda expression func


    【解决方案1】:

    您的成员信息的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();
        }
    }
    

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

    【讨论】:

      【解决方案2】:

      如果你想要类型安全,你可以传入原始

      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);
      

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多