有没有一个好的实用类建议,可以将一个对象的值映射到另一个对象上?我想要一个使用反射机制的实用类,接受两个对象并在第二个对象中复制第一个对象中具有相同名称的公共属性的值。
我有两个实体是从Web服务代理生成的,所以我不能更改父类或实现接口或做任何类似的操作。但我知道这两个对象拥有相同的公共属性。
有没有一个好的实用类建议,可以将一个对象的值映射到另一个对象上?我想要一个使用反射机制的实用类,接受两个对象并在第二个对象中复制第一个对象中具有相同名称的公共属性的值。
我有两个实体是从Web服务代理生成的,所以我不能更改父类或实现接口或做任何类似的操作。但我知道这两个对象拥有相同的公共属性。
应该很简单就能组合在一起...
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;
}
}
}
}
Jon Skeet和Marc Gravell有一个名为MiscUtil的库。在MiscUtil.Reflection中,有一个名为PropertyCopy
的类,正好可以做到你所描述的内容。它只适用于.NET 3.5。
它通过遍历SourceType的公共属性并按名称与TargetType的公共属性匹配,确保每个属性都可以从源分配给目标,然后为这两种类型创建和缓存复制函数(这样你就不必每次进行所有这些反射)。我已经在生产代码中使用过它,并且可以证明它的好处。
算了,我想我会直接发布他们简明的代码(带有少于100行的注释)。此代码的许可证可以在此处找到:
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( 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;
}
}
}
}
}
使用Json.net怎么样呢?
static T CopyPropertiesJson<T>(object source)
{
string jsonsource = JsonConvert.SerializeObject(source);
return JsonConvert.DeserializeObject<T>(jsonsource);
}
Expression<T>
来做这件事的博客。Expression<>
比较对象属性的代码之后,我被邀请撰写了这篇文章。 我可能错了,但很可能是这个链接:https://dev59.com/9HNA5IYBdhLWcg3wX8rk#986617。 - Marc Gravell
AutoMapper
和Expression
这样的方法会在运行时将其预编译成 IL,这可能会带来显着的性能优势。因此,如果您需要频繁进行此操作,请避免使用基本的反射方法。 - Marc Gravell