c# - 将一个对象属性值传输到另一个对象

标签 c# mapping reflection.emit system.reflection ilgenerator

毕竟,我知道 AutoMapper,但我不想使用它。因为我正在学习 C# 并且我想深入了解它。所以我正在尝试自己解决这个问题(在下面解释)。

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

    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.EmitILGeneratorlate-bound delegates 是更快,性能更高。但没有更多的解释或任何链接。你能帮我理解加速这段代码的方法吗?或者你能给我一些关于 EmitILGeneratorlate-bound delegates 的链接吗?或者您认为可以帮助我接受主题的任何内容?

完成问题:

我从@svick 的回答中理解并学到了很多东西。但是现在,如果我想把它作为一个开放的泛型方法来使用,我该怎么办呢?像这样:

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 通常要容易得多s 并且它为您提供基本相同的性能。请记住,只有缓存编译后的代码才能获得性能优势,例如 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);
}

如果你有这个,编写你的泛型方法就很简单了:

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> 的东西。并在从缓存中检索委托(delegate)后将其转换为正确的类型。

关于c# - 将一个对象属性值传输到另一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9762190/

相关文章:

c# - 最佳实践 : new DateTime() vs DateTime. Parse()

c# - 收集没有重复但有顺序,快速包含检查和添加/删除

asp.net-mvc - EF 映射对象的不兼容数据读取器异常

c# - OpCodes.Ret 使用的地址存储在哪里?可以改变吗?

c# - IL 使用 Reflection.Emit 调用带有 2 个数组参数的方法

c# - 网页作为调度程序?

c# - XmlSerializer 的多个 namespace

iis - IIS 在哪里存储有关每个绑定(bind)的 SSL 证书协议(protocol)映射的信息?

mvvm - 使用 knockout 映射插件将深度分层的对象映射到自定义类

c# - 反射发射 : how to Convert Attribute instance to CustomAttributeBuilder or CustomAttributeData