将值从一个对象复制到另一个对象
任何人都有一个好的工具类的build议,将一个对象的值映射到另一个对象? 我想要一个实用工具类,使用reflection,并采用两个对象,并从第一个对象的值复制到第二个,如果有一个同名的公共属性。
我有两个从Web服务代理生成的实体,所以我不能改变父类或impliment接口或类似的东西。 但是我知道这两个对象具有相同的公共属性。
Jon Skeet和Marc Gravell有一个名为MiscUtil的图书馆。 在MiscUtil.Reflection
里面有一个名为PropertyCopy
的类,它完全按照你所描述的。 它只适用于.NET 3.5。
它通过运行SourceType的公共属性进行工作,通过名称与TargetType的公共属性进行匹配,确保每个属性都可以从源分配给目标,然后为这两个目标创build和caching复制functiontypes(所以你不要每次都做这个reflection)。 我已经在生产代码中使用它,并可以保证它的善良。
嘿,我想我只是发表他们简洁的代码(它不到100行/评论)。 这个代码的许可证可以在这里find:
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(); } } } }
应该很简单,扔在一起…
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; } } } }
我们使用Automapper来做到这一点。 它工作得很好。
我改进了Robinson的答案,并将其重构为对象types的扩展方法,非常方便:
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; } } } } }
我在阅读Marc Gravell的这篇文章后不久就使用Expression<T>
做了这个 。
它看起来(基于另一个答案),就像它可能类似于Jon Skeet和Marc的MiscUtil。
那么用Json.net做什么呢?
static T CopyPropertiesJson<T>(object source) { string jsonsource = JsonConvert.SerializeObject(source); return JsonConvert.DeserializeObject<T>(jsonsource); }