Tôi có một kiểu MiscUtil
được gọi là PropertyCopy
thực hiện một cái gì đó tương tự - mặc dù nó tạo ra một thể hiện mới của kiểu đích và sao chép các thuộc tính vào đó.
Nó không yêu cầu các kiểu phải giống nhau - nó chỉ sao chép tất cả các thuộc tính có thể đọc được từ kiểu "nguồn" sang kiểu "đích". Tất nhiên nếu các loại giống nhau, điều đó có nhiều khả năng hoạt động hơn :) Đó là một bản sao nông cạn, btw.
Trong khối mã ở cuối câu trả lời này, tôi đã mở rộng khả năng của lớp. Để sao chép từ thể hiện này sang thể hiện khác, nó sử dụng PropertyInfo
các giá trị đơn giản tại thời điểm thực thi - điều này chậm hơn so với sử dụng cây biểu thức, nhưng cách thay thế sẽ là viết một phương thức động, điều này tôi không quá nóng. Nếu hiệu suất hoàn toàn quan trọng đối với bạn, hãy cho tôi biết và tôi sẽ xem mình có thể làm gì. Để sử dụng phương thức này, hãy viết một cái gì đó như:
MyType instance1 = new MyType();
MyType instance2 = new MyType();
PropertyCopy.Copy(instance1, instance2);
(đâu Copy
là một phương pháp chung được gọi là sử dụng kiểu suy luận).
Tôi chưa thực sự sẵn sàng để phát hành MiscUtil đầy đủ, nhưng đây là mã được cập nhật, bao gồm cả nhận xét. Tôi sẽ không viết lại chúng cho trình soạn thảo SO - chỉ cần sao chép toàn bộ.
(Tôi cũng có thể thiết kế lại API một chút về cách đặt tên nếu tôi bắt đầu từ đầu, nhưng tôi không muốn phá vỡ những người dùng hiện tại ...)
#if DOTNET35
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace MiscUtil.Reflection
{
public static class PropertyCopy
{
public static void Copy<TSource, TTarget>(TSource source, TTarget target)
where TSource : class
where TTarget : class
{
PropertyCopier<TSource, TTarget>.Copy(source, target);
}
}
public static class PropertyCopy<TTarget> where TTarget : class, new()
{
public static TTarget CopyFrom<TSource>(TSource source) where TSource : class
{
return PropertyCopier<TSource, TTarget>.Copy(source);
}
}
internal static class PropertyCopier<TSource, TTarget>
{
private static readonly Func<TSource, TTarget> creator;
private static readonly List<PropertyInfo> sourceProperties = new List<PropertyInfo>();
private static readonly List<PropertyInfo> targetProperties = new List<PropertyInfo>();
private static readonly Exception initializationException;
internal static TTarget Copy(TSource source)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
return creator(source);
}
internal static void Copy(TSource source, TTarget target)
{
if (initializationException != null)
{
throw initializationException;
}
if (source == null)
{
throw new ArgumentNullException("source");
}
for (int i = 0; i < sourceProperties.Count; i++)
{
targetProperties[i].SetValue(target, sourceProperties[i].GetValue(source, null), null);
}
}
static PropertyCopier()
{
try
{
creator = BuildCreator();
initializationException = null;
}
catch (Exception e)
{
creator = null;
initializationException = e;
}
}
private static Func<TSource, TTarget> BuildCreator()
{
ParameterExpression sourceParameter = Expression.Parameter(typeof(TSource), "source");
var bindings = new List<MemberBinding>();
foreach (PropertyInfo sourceProperty in typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
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.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
{
throw new ArgumentException("Property " + sourceProperty.Name + " is static 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)));
sourceProperties.Add(sourceProperty);
targetProperties.Add(targetProperty);
}
Expression initializer = Expression.MemberInit(Expression.New(typeof(TTarget)), bindings);
return Expression.Lambda<Func<TSource, TTarget>>(initializer, sourceParameter).Compile();
}
}
}
#endif