transferring one object properties values to another one

我怕爱的太早我们不能终老 提交于 2019-12-03 16:35:47

You could use Reflection.Emit to do this, but it's usually much easier to use Expressions and it gives you basically the same performance. Keep in mind that the performance benefit is there only if you cache the compiled code, for example in Dictionary<Tuple<Type, Type>, Action<object, object>>, which I'm not doing here.

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);
}

If you have this, writing your generic method is simpple:

public TTarget Transfer<TSource, TTarget>(TSource source)
    where TTarget : class, new()
{
    var target = new TTarget();
    Transfer(source, target);
    return target;
} 

It could make sense to make the main worker method generic too and create Action<TSource, TTarget>, or even let it directly create the object and use Func<TSource, TTarget>. But if added caching as I suggested, it would mean you would have to use something like Dictionary<Tuple<Type, Type>, Delegate> and cast the delegate to the right type after retrieving it from the cache .

You might consider only getting the properties (by name) that match on the target. That will significantly simplify your code.

foreach (var property in sourceType.GetProperties( BindingFlags.Public | BindingFlags.Instance))
{
     var targetProperty = targetType.GetProperty( property.Name, BindingFlags.Public | BindingFlags.Instance );
     if (targetProperty != null
          && targetProperty.CanWrite
          && targetProperty.PropertyType.IsAssignableFrom(property.PropertyType))
     {
         targetProperty.SetValue( target, property.GetValue(source, null), null );
     }
}
Wiktor Zychla

C# Reflection IL - Understanding how values are copied

The question is about cloning so the type of the source object is the same as the type of the target object (while as I understand you can have different types in source and target) but still this could be worth of analyzing.

I wrote a blog post about how to do it (portuguese only, but you could read the code)

http://elemarjr.net/2012/02/27/um-helper-para-shallow-cloning-emitting-em-c/

You can get the code from:

https://github.com/ElemarJR/FluentIL/blob/master/demos/Cloning/src/Cloning/Cloning/ILCloner.cs

I think that to use Reflection.Emit is harder than needed. So, I wrote a open-source library, called FluentIL (www.fluentil.org) to make it easier. I used it here.

[]s

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!