Вы могли бы использовать Reflection.Emit, чтобы сделать это, но обычно гораздо проще использовать Expression
с, и это дает вам в основном ту же производительность.Имейте в виду, что выигрыш в производительности есть только в том случае, если вы кешируете скомпилированный код, например, в Dictionary<Tuple<Type, Type>, Action<object, object>>
, чего я здесь не делаю.
static void Transfer(object source, object target)
{
var sourceType = source.GetType();
var targetType = target.GetType();
var sourceParameter = Expression.Parameter(typeof(object), "source");
var targetParameter = Expression.Parameter(typeof(object), "target");
var sourceVariable = Expression.Variable(sourceType, "castedSource");
var targetVariable = Expression.Variable(targetType, "castedTarget");
var expressions = new List<Expression>();
expressions.Add(Expression.Assign(sourceVariable, Expression.Convert(sourceParameter, sourceType)));
expressions.Add(Expression.Assign(targetVariable, Expression.Convert(targetParameter, targetType)));
foreach (var property in sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (!property.CanRead)
continue;
var targetProperty = targetType.GetProperty(property.Name, BindingFlags.Public | BindingFlags.Instance);
if (targetProperty != null
&& targetProperty.CanWrite
&& targetProperty.PropertyType.IsAssignableFrom(property.PropertyType))
{
expressions.Add(
Expression.Assign(
Expression.Property(targetVariable, targetProperty),
Expression.Convert(
Expression.Property(sourceVariable, property), targetProperty.PropertyType)));
}
}
var lambda =
Expression.Lambda<Action<object, object>>(
Expression.Block(new[] { sourceVariable, targetVariable }, expressions),
new[] { sourceParameter, targetParameter });
var del = lambda.Compile();
del(source, target);
}
Если у вас это есть, пишите свой универсальный методis simpple:
public TTarget Transfer<TSource, TTarget>(TSource source)
where TTarget : class, new()
{
var target = new TTarget();
Transfer(source, target);
return target;
}
Возможно, имеет смысл также сделать основной рабочий метод универсальным и создать Action<TSource, TTarget>
, или даже позволить ему напрямую создать объект и использовать Func<TSource, TTarget>
.Но если бы я добавил кеширование, как я предлагал, это означало бы, что вам нужно будет использовать что-то вроде Dictionary<Tuple<Type, Type>, Delegate>
и привести делегат к нужному типу после извлечения его из кеша.