2012-03-18 61 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

相关问题