2015-09-03 45 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>雖然,你可能需要一個額外的演員。

相關問題