У мене є тип в MiscUtil
викликається, PropertyCopy
який робить щось подібне - хоча він створює новий екземпляр цільового типу і копіює властивості в нього.
Він не вимагає, щоб типи були однаковими - він просто копіює всі читабельні властивості з типу "джерело" у тип "ціль". Звичайно, якщо типи однакові, це, швидше за все, спрацює :) Це неглибока копія, до речі.
У блоці коду внизу цієї відповіді я розширив можливості класу. Для копіювання з одного екземпляра в інший він використовує прості PropertyInfo
значення під час виконання - це повільніше, ніж використання дерева виразів, але альтернативою може бути написання динамічного методу, на якому я не надто гарячий. Якщо продуктивність для вас абсолютно критична, дайте мені знати, і я побачу, що я можу зробити. Щоб скористатися методом, напишіть щось на зразок:
MyType instance1 = new MyType();
MyType instance2 = new MyType();
PropertyCopy.Copy(instance1, instance2);
(де Copy
- загальний метод, що називається за допомогою висновку типу).
Я насправді не готовий зробити повний випуск MiscUtil, але ось оновлений код, включаючи коментарі. Я не збираюся переробляти їх для редактора SO - просто скопіюйте весь шматок.
(Я б також, можливо, трохи переробив API з точки зору імен, якщо я починав з нуля, але я не хочу зламати існуючих користувачів ...)
#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