2015-09-03 103 views
0

我想写一个方法,它接受两个MemberExpression,并生成一个委托,它接受两个对象 - source和target,并根据它的MemberExpression将来源的值赋给目标字段,根据第二个MemberExpression。这些对象不必是同一类型的。 我正在寻找这样的事情:如何根据另一个MemberExpression将MemberExpression中的值赋给一个字段?

public Action<TSource, TTarget> Map(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter) 
{ 
    var sourceField = getter.Body as MemberExpression; 
    var targetField = setter.Body as MemberExpression; 

    /* 
    * Now I would like to create a lambda expression which accepts TSource and TTarget instances, 
    * and assings TTarget according to the above getter and setter expressions. Kind of like: 
    * var assignExp = Expression.Assign(x, y); 
    * var lambda = Expression.Lambda<Action<TTarget, TSource>>(....).Compile(); 
    * return lambda; 
    */ 

} 

用法:

Target target; 
Source source; 
//... 
var action = Map(p => p.NestedField.Dummy, x => x.TargetName); 
action(source, target); 

我不明白如何构建表情发送给Expression.Assign

在这一点上,我不介意空值或字段的初始化。请假设所有字段都已初始化。

+1

那么你已经注释掉了代码......当你尝试这些时会发生什么,但是用'targetField'和'sourceField'代替'x'和'y'? –

回答

1

赋值用于生成赋值表达式,但在您的情况下,每个lambda表达式都有自己的参数,并且这两个参数都应发送到新的lambda表达式。

所以在我的例子中,我生成新的赋值表达式,然后创建一个新的lambda表达式,并从getter和setter表达式发送​​到一个新的lambda。

因此,它应该是这样的:

这里工作样本 - https://dotnetfiddle.net/uuPVAl和代码本身

using System; 
using System.Linq.Expressions; 

public class Program 
{ 
    public static void Main(string[] args) 
    { 
     Target target = new Target(); 

     Source source = new Source() 
     { 
      NestedField = new NestedSource() 
      { 
       Dummy = "Hello world" 
      } 
     }; 


     var action = Map<Source, Target>(p => p.NestedField.Dummy, x => x.TargetName); 
     action(source, target); 

     Console.WriteLine(target.TargetName); 
    } 

    public static Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter) 
    { 
     var sourceField = getter.Body as MemberExpression; 
     var targetField = setter.Body as MemberExpression; 

     // here we create new assign expression 
     var assign = Expression.Assign(targetField, sourceField); 

     // and then compile it with original two parameters 
     var lambda = Expression.Lambda<Action<TSource, TTarget>>(assign, getter.Parameters[0], setter.Parameters[0]); 
     return lambda.Compile(); 
    } 
} 

public class Target 
{ 
    public string TargetName { get; set; } 
} 

public class NestedSource 
{ 
    public string Dummy { get; set; } 
} 

public class Source 
{ 
    public NestedSource NestedField { get; set; } 
} 

UPDATE

因此,每个Lambda表达式可以有任何参数。从代码方面看,这是​​。当您将表达式编写为典型代码时,则表示函数参数,因此在您的情况下,(p) => p.NestedField.Dummy - (p)是该函数的参数。而体内表达使用它 - p.NestedField.Dummy,所以要编译它 - lambda表达式需要知道该参数。

在这种情况下,您有两个用于目标和来源的lambda表达式,并且每个都有自己的参数 - (p)(x),每个表达式都使用自己的参数。但是在结果函数中我们需要使用它们,因为我们在函数中有两个参数,所以我们需要从源和目标重新发送原始的​​到一个新的lambda。或者你可以创建一个新的​​,但你需要创建一个新的树,因为旧的将使用旧的​​。通常这样的事情是用ExpressionVisitor类完成的,但在你的情况下,我们只需重新发送原始表达式而不需要更改树体。

+0

非常感谢!你能否详细说明'X.Parameters []'?试图谷歌它,但太少有关于它的信息= \ –

+1

当然。补充更新 –

+1

感谢万万:) –

1

这样做:

public Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter) 
    { 
     var targetPropertyExpression = setter.Body as MemberExpression; 
     var targetProperty = targetPropertyExpression.Member as PropertyInfo; 

     return (src, tgt) => { targetProperty.SetValue(tgt, getter.Compile().Invoke(src)); }; 
    } 

它得到的从第1个lambda表达式的setter的属性和方法会返回一个动作,其值分配给基于第二lambda表达式,它只是需要的属性被调用。

照顾<TSource, object>虽然,你可能需要一个额外的演员。

相关问题