2010-04-12 147 views
14

任何人都有一个好的工具类的建议,可以将一个对象的值映射到另一个对象上?我想要一个实用程序类,它使用反射并获取两个对象,并将第一个对象的值复制到第二个对象,如果存在具有相同名称的公共属性的话。将值从一个对象复制到另一个对象

我有两个从Web服务代理生成的实体,所以我不能改变父类或impliment接口或类似的东西。但我知道这两个对象具有相同的公共属性。

+5

只是为了记录 - 接受的答案比其他人有更多的开销,因为它使用原始反射。像'AutoMapper'和'Expression'这样的方法可以在运行时将其预编译为IL,这可以产生*显着的性能优势。所以如果你正在做很多这样的事情,也许应该避免使用基本的反思方法。 – 2010-04-13 05:18:03

回答

18

Jon Skeet和Marc Gravell有一个名为MiscUtil的图书馆。在MiscUtil.Reflection里面有一个名为PropertyCopy的类,它完全符合你的描述。它只适用于.NET 3.5。

它通过运行SourceType的公共属性进行工作,通过名称与TargetType的公共属性进行匹配,确保每个属性都可以从源分配给目标,然后创建并缓存复制器函数为这两种类型(所以你不要每次都做这种反射)。我已经在生产代码中使用它,并可以保证其良好性。

什么哎,我想我只是张贴他们简洁的代码(这是不到100线W /评论)。此代码的许可证,可以发现here

using System; 
using System.Collections.Generic; 
using System.Linq.Expressions; 
using System.Reflection; 

namespace MiscUtil.Reflection 
{ 
    /// <summary> 
    /// Generic class which copies to its target type from a source 
    /// type specified in the Copy method. The types are specified 
    /// separately to take advantage of type inference on generic 
    /// method arguments. 
    /// </summary> 
    public static class PropertyCopy<TTarget> where TTarget : class, new() 
    { 
     /// <summary> 
     /// Copies all readable properties from the source to a new instance 
     /// of TTarget. 
     /// </summary> 
     public static TTarget CopyFrom<TSource>(TSource source) where TSource : class 
     { 
      return PropertyCopier<TSource>.Copy(source); 
     } 

     /// <summary> 
     /// Static class to efficiently store the compiled delegate which can 
     /// do the copying. We need a bit of work to ensure that exceptions are 
     /// appropriately propagated, as the exception is generated at type initialization 
     /// time, but we wish it to be thrown as an ArgumentException. 
     /// </summary> 
     private static class PropertyCopier<TSource> where TSource : class 
     { 
      private static readonly Func<TSource, TTarget> copier; 
      private static readonly Exception initializationException; 

      internal static TTarget Copy(TSource source) 
      { 
       if (initializationException != null) 
       { 
        throw initializationException; 
       } 
       if (source == null) 
       { 
        throw new ArgumentNullException("source"); 
       } 
       return copier(source); 
      } 

      static PropertyCopier() 
      { 
       try 
       { 
        copier = BuildCopier(); 
        initializationException = null; 
       } 
       catch (Exception e) 
       { 
        copier = null; 
        initializationException = e; 
       } 
      } 

      private static Func<TSource, TTarget> BuildCopier() 
      { 
       ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source"); 
       var bindings = new List<MemberBinding>(); 
       foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties()) 
       { 
        if (!sourceProperty.CanRead) 
        { 
         continue; 
        } 
        PropertyInfo targetProperty = typeof(TTarget).GetProperty(sourceProperty.Name); 
        if (targetProperty == null) 
        { 
         throw new ArgumentException("Property " + sourceProperty.Name + " is not present and accessible in " + typeof(TTarget).FullName); 
        } 
        if (!targetProperty.CanWrite) 
        { 
         throw new ArgumentException("Property " + sourceProperty.Name + " is not writable in " + typeof(TTarget).FullName); 
        } 
        if (!targetProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) 
        { 
         throw new ArgumentException("Property " + sourceProperty.Name + " has an incompatible type in " + typeof(TTarget).FullName); 
        } 
        bindings.Add(Expression.Bind(targetProperty, Expression.Property(sourceParameter, sourceProperty))); 
       } 
       Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings); 
       return Expression.Lambda<Func<TSource,TTarget>>(initializer, sourceParameter).Compile(); 
      } 
     } 
    } 
} 
+9

我们在看着你;-p – 2010-04-12 20:50:51

+1

@Marc Gravell:就像光明会? :) – 2010-04-12 21:10:57

+0

有点像这样,但他们没有♦;-p(除非Jeff *是Illuminati ...) – 2010-04-12 21:27:01

38

应该是很简单的拼凑...

public static void CopyPropertyValues(object source, object destination) 
{ 
    var destProperties = destination.GetType().GetProperties(); 

    foreach (var sourceProperty in source.GetType().GetProperties()) 
    { 
     foreach (var destProperty in destProperties) 
     { 
      if (destProperty.Name == sourceProperty.Name && 
     destProperty.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)) 
      { 
       destProperty.SetValue(destination, sourceProperty.GetValue(
        source, new object[] { }), new object[] { }); 

       break; 
      } 
     } 
    } 
} 
+1

这对我工作,但我添加了只读属性的代码行。 – 2013-05-06 06:27:46

+0

适合我。投票!伟大的提示!;) – 2013-09-05 11:32:44

+0

As @VincentDagpin在'if'语句中声明'&& destProperty.CanWrite'为只读道具 – KregHEk 2015-07-24 05:04:53

11

我们使用Automapper这一点。它工作得很好。

+0

错误。让我永远记住它是“AutoMapper”而不是“AutoMap”。顺便说一句,搜索谷歌吸吮“自动地图”。 – Will 2010-04-12 19:44:03

2

我在博客this article的Marc Gravell后不久写了约using Expression<T> to do this

它看起来(基于另一个答案),就像它可能类似于Jon Skeet和Marc的MiscUtil。

+0

有趣的是,我被邀请在有人喜欢基于'Expression <>'的代码之后写这篇文章,该代码比较每个属性的属性。我可能是错的,但可能是这一个:http://stackoverflow.com/questions/986572/hows-to-quick-check-if-data-transfer-two-objects-have-equal-properties-in-c/ 986617#986617。 – 2010-04-12 20:51:55

+0

404 :-( 是否有更新的URL? – Alex 2017-11-21 17:25:55

+0

@jitbit固定的URL – 2017-11-21 18:22:49

5

我已经改善罗宾逊的答案,并重构它变成一个扩展方法对象类型,非常方便:

public static void CopyPropertyValues(this object destination, object source) 
    { 
     if (!(destination.GetType().Equals (source.GetType()))) 
      throw new ArgumentException ("Type mismatch"); 
     if (destination is IEnumerable) 
     { 
      var dest_enumerator = (destination as IEnumerable).GetEnumerator(); 
      var src_enumerator = (source as IEnumerable).GetEnumerator(); 
      while (dest_enumerator.MoveNext() && src_enumerator.MoveNext()) 
       dest_enumerator.Current.CopyPropertyValues (src_enumerator.Current); 
     } 
     else 
     { 
      var destProperties = destination.GetType().GetRuntimeProperties(); 
      foreach (var sourceProperty in source.GetType().GetRuntimeProperties()) 
      { 
       foreach (var destProperty in destProperties) 
       { 
        if (destProperty.Name == sourceProperty.Name 
         && destProperty.PropertyType.GetTypeInfo() 
          .IsAssignableFrom (sourceProperty.PropertyType.GetTypeInfo())) 
        { 
         destProperty.SetValue (destination, sourceProperty.GetValue (
          source, new object[] { }), new object[] { }); 
         break; 
        } 
       } 
      } 
     } 
    } 
+0

如果有任何属性是只读的或只写的,则会抛出 – Yaur 2013-06-15 02:17:13

+0

假设我们有一个对象列表,我们在while,dest_enumerator.Current之后得到一个异常,.......... “枚举要么未开始,要么已经完成。” 您的代码不起作用。 – Jahan 2014-10-17 19:00:15

0

什么用Json.net这样做呢?

static T CopyPropertiesJson<T>(object source) 
{ 
    string jsonsource = JsonConvert.SerializeObject(source); 
    return JsonConvert.DeserializeObject<T>(jsonsource); 
} 
相关问题