将一个对象的属性值转移到另一个 [英] transferring one object properties values to another one

查看:143
本文介绍了将一个对象的属性值转移到另一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我了解 AutoMapper ,并且我不想使用它.因为我正在学习 C#,并且想深入了解它.因此,我正在尝试自己解决此问题(如下所述).

Before all, I know about AutoMapper, and I don't want to use it. Because I'm learning C# and I want to receive a deep view of it. So I'm trying to do this issue (explained below) myself.

但是,如果该属性具有相同的名称和类型,并且可以从源中读取并且可以在目标中写入,则我试图创建一个属性复制器以将一种类型的属性的值处理为另一种.我正在使用type.GetProperties()方法.采样方法在这里:

However, I'm trying to create a property copier to cope values of one type's properties to another one, if the property has the same name and type and is readable from source and writable in target. I'm using type.GetProperties() method. Sampled method is here:

    static void Transfer(object source, object target) {

        var sourceType = source.GetType();
        var targetType = target.GetType();

        var sourceProps = sourceType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        var targetProps = (from t in targetType.GetProperties()
                           where t.CanWrite
                                 && (t.GetSetMethod().Attributes & MethodAttributes.Static) == 0
                           select t).ToList();

        foreach(var prop in sourceProps) {
            var value = prop.GetValue(source, null);
            var tProp = targetProps
                .FirstOrDefault(p => p.Name == prop.Name &&
                    p.PropertyType.IsAssignableFrom(prop.PropertyType));
            if(tProp != null)
                tProp.SetValue(target, value, null);
        }
    }

它有效,但是我在SO上读到一个答案,即使用System.Reflection.EmitILGenerator以及后期绑定委托更快,并且性能更高.但是没有更多的解释或任何链接.您可以帮助我了解加快此代码速度的方法吗?还是可以建议我一些有关EmitILGenerator后期绑定委托的链接?还是您认为有什么要帮助我学科的?

It works, but I read an answer at SO, that using System.Reflection.Emit and ILGenerator and late-bound delegates are more quickly and have a higher performance. But there was not more explanation or any link. Can you help me to understanding ways to speed up this code? or can you suggest me some links about Emit, ILGenerator, and late-bound delegates please? Or anything you think will help me to subject?

COMPELETE Q:

我了解并从@svick的答案中学到很多东西.但是现在,如果我想将其用作开放的泛型方法,该怎么办?像这样的东西:

I understand and learn many things from @svick's answer. But now, if I want to use it as an open generic method, how can I do it? something like this:

public TTarget Transfer<TSource, TTarget>(TSource source) where TTarget : class, new() { } 

或扩展名:

public static TTarget Transfer<TSource, TTarget>(this TSource source) where TTarget : class, new() { } 

推荐答案

可以使用Reflection.Emit做到这一点,但是使用Expression通常更容易,并且基本上可以为您提供相同的性能.请记住,只有在缓存编译后的代码时(例如在Dictionary<Tuple<Type, Type>, Action<object, object>>中,我才不在这里进行缓存),才有性能上的好处.

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

也可以使main worker方法也通用并创建Action<TSource, TTarget>,或者甚至直接创建对象并使用Func<TSource, TTarget>.但是,如果按照我的建议添加缓存,则意味着您必须使用Dictionary<Tuple<Type, Type>, Delegate>之类的东西,然后从缓存中检索委托,然后将委托转换为正确的类型.

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 .

这篇关于将一个对象的属性值转移到另一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆