2012-03-18 54 views
4

一切之前,我知道AutoMapper,我不希望使用它。因爲我在學習C#,我想深入瞭解它。所以我試圖自己做這個問題(下面解釋)。transfering一個對象的屬性值,另一個

不過,我試圖創建一個屬性複印機應付的一個類型的屬性值,以一個又一個,如果屬性具有相同的名稱和類型,是從源頭和可寫在目標可讀。我正在使用type.GetProperties()方法。抽樣方法是在這裏:

static void Transfer(object source, object target) { 

     var sourceType = source.GetType(); 
     var targetType = target.GetType(); 

     var sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance); 

     var targetProps = (from t in targetType.GetProperties() 
          where t.CanWrite 
           && (t.GetSetMethod().Attributes & MethodAttributes.Static) == 0 
          select t).ToList(); 

     foreach(var prop in sourceProps) { 
      var value = prop.GetValue(source, null); 
      var tProp = targetProps 
       .FirstOrDefault(p => p.Name == prop.Name && 
        p.PropertyType.IsAssignableFrom(prop.PropertyType)); 
      if(tProp != null) 
       tProp.SetValue(target, value, null); 
     } 
    } 

它的工作原理,但我在SO讀一個答案,即使用System.Reflection.EmitILGenerator後期綁定代表更迅速,具有更高的性能。但沒有更多的解釋或任何聯繫。你能幫我理解加速這段代碼的方法嗎?或者你可以建議我約EmitILGenerator一些鏈接,並後期綁定代表好嗎?或者你認爲的任何事情都會幫助我學習?提前致謝。

COMPELETE問:

我瞭解和@ svick的答案學到很多東西。但是現在,如果我想用它作爲一個開放的泛型方法,我該怎麼做呢?是這樣的:

public TTarget Transfer<TSource, TTarget>(TSource source) where TTarget : class, new() { } 

或擴展:

public static TTarget Transfer<TSource, TTarget>(this TSource source) where TTarget : class, new() { } 
+1

我不認爲System.Reflection.Emit會幫你在這裏。在你的情況下,源和目標對象都是在編譯時存在的,你只是將相應屬性的值從一個拷貝到另一個。如果您希望(例如)在運行時創建目標類型,則Emit將幫助您。 – 2012-03-18 21:07:05

回答

3

你可能會考慮只獲得屬性(按名稱)的目標相匹配的。這將顯着簡化您的代碼。

foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
{ 
    var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); 
    if (targetProperty != null 
      && targetProperty.CanWrite 
      && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) 
    { 
     targetProperty.SetValue(target, property.GetValue(source, null), null); 
    } 
} 
+0

好主意。所以謝謝 – 2012-03-18 21:12:35

+0

我認爲你的代碼不會工作,如果'源'有一些只寫屬性。他們非常罕見,是一種不好的做法,但當然可能。 – svick 2012-03-18 21:28:49

+0

@svick在模型映射中(ref,OP的介紹)我認爲假定源和目標都沒有隻寫屬性是可以接受的。 – tvanfosson 2012-03-18 22:02:13

5

可能使用Reflection.Emit來做到這一點,但它通常更容易使用Expression s,它給你基本上相同的性能。請記住,性能優勢是存在的前提是你緩存編譯後的代碼,例如Dictionary<Tuple<Type, Type>, Action<object, object>>,這我不會在這裏做。

static void Transfer(object source, object target) 
{ 
    var sourceType = source.GetType(); 
    var targetType = target.GetType(); 

    var sourceParameter = Expression.Parameter(typeof(object), "source"); 
    var targetParameter = Expression.Parameter(typeof(object), "target"); 

    var sourceVariable = Expression.Variable(sourceType, "castedSource"); 
    var targetVariable = Expression.Variable(targetType, "castedTarget"); 

    var expressions = new List<Expression>(); 

    expressions.Add(Expression.Assign(sourceVariable, Expression.Convert(sourceParameter, sourceType))); 
    expressions.Add(Expression.Assign(targetVariable, Expression.Convert(targetParameter, targetType))); 

    foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) 
    { 
     if (!property.CanRead) 
      continue; 

     var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance); 
     if (targetProperty != null 
       && targetProperty.CanWrite 
       && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType)) 
     { 
      expressions.Add(
       Expression.Assign(
        Expression.Property(targetVariable, targetProperty), 
        Expression.Convert(
         Expression.Property(sourceVariable, property), targetProperty.PropertyType))); 
     } 
    } 

    var lambda = 
     Expression.Lambda<Action<object, object>>(
      Expression.Block(new[] { sourceVariable, targetVariable }, expressions), 
      new[] { sourceParameter, targetParameter }); 

    var del = lambda.Compile(); 

    del(source, target); 
} 

如果你有這樣的,寫你的泛型方法是simpple:

public TTarget Transfer<TSource, TTarget>(TSource source) 
    where TTarget : class, new() 
{ 
    var target = new TTarget(); 
    Transfer(source, target); 
    return target; 
} 

這可能是有意義的使主輔助方法一般也創造Action<TSource, TTarget>,甚至讓它直接創建對象並使用Func<TSource, TTarget>。但是,如果加入緩存,因爲我認爲,這將意味着你將不得不使用像Dictionary<Tuple<Type, Type>, Delegate>並從緩存中檢索之後投的委託權類型。

+0

我幾乎理解你的代碼;但是因爲我是C#的新手,所以有些東西對我來說還是模糊的。你能解釋我怎樣才能用這個開放式的泛型?像這樣:'TTarget Transfer (TTarget target)其中TTarget:class,new()'我把它放在原始問題中。非常感謝 – 2012-03-18 22:47:39

+0

@ king.net,請參閱編輯。 – svick 2012-03-19 00:26:48

+0

感謝編輯和指導。是的,我明白你在說什麼,我知道這件事。但我的問題是獲得泛型類型的類型和屬性,以及如何爲它們創建表達式。例如,它看起來像這些行:'var sourceParameter = Expression.Parameter(typeof(object),「source」); var targetParameter = Expression.Parameter(typeof(object),「target」);'如果方法是一個開放的泛型方法,則不需要。是對的? – 2012-03-19 11:28:50

相關問題