繁体   English   中英

如何根据另一个MemberExpression从MemberExpression分配一个值到字段?

[英]How to assign a value from MemberExpression to a field according to another MemberExpression?

我想编写一个接受两个MemberExpression的方法,并生成一个接受两个对象的委托-源和目标,并根据第二个MemberExpression根据它的MemberExpression将来自源的值分配给目标的字段。 这些对象不必具有相同的类型。 我正在寻找这样的东西:

public Action<TSource, TTarget> Map(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
{
    var sourceField = getter.Body as MemberExpression;
    var targetField = setter.Body as MemberExpression;

    /*
     * Now I would like to create a lambda expression which accepts TSource and TTarget instances,
     * and assings TTarget according to the above getter and setter expressions. Kind of like:
     * var assignExp = Expression.Assign(x, y);
     * var lambda = Expression.Lambda<Action<TTarget, TSource>>( .... ).Compile();
     * return lambda;
     */

} 

用法:

Target target;
Source source;
//... 
var action = Map(p => p.NestedField.Dummy, x => x.TargetName);
action(source, target);

我不明白如何构建要发送到Expression.Assign

在这一点上,我不介意空值或字段初始化。 请假设所有字段都已初始化。

这样可以:

public Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
    {
        var targetPropertyExpression = setter.Body as MemberExpression;
        var targetProperty = targetPropertyExpression.Member as PropertyInfo;

        return (src, tgt) => { targetProperty.SetValue(tgt, getter.Compile().Invoke(src)); };
    } 

它从第一个lambda表达式获取setter的属性,并仅返回一个操作,该操作将根据需要调用的第二个lambda表达式将值分配给该属性。

不过,请注意<TSource, object> ,您可能需要额外的强制转换。

Assign用于生成Assign表达式,但是在您的情况下,每个Lambda表达式都有自己的参数,并且这两个参数都应发送到新的Lambda表达式。

因此,在我的示例中,我生成一个新的assign表达式,然后创建一个新的lambda表达式,并将ParameterExpression从getter和setter表达式发送到一个新的lambda。

所以应该是这样的:

这是工作示例-https: //dotnetfiddle.net/uuPVAl和代码本身

using System;
using System.Linq.Expressions;

public class Program
{
    public static void Main(string[] args)
    {
        Target target = new Target();

        Source source = new Source()
        {
            NestedField = new NestedSource()
            {
                Dummy = "Hello world"
            }
        };


        var action = Map<Source, Target>(p => p.NestedField.Dummy, x => x.TargetName);
        action(source, target);

        Console.WriteLine(target.TargetName);
    }

    public static Action<TSource, TTarget> Map<TSource, TTarget>(Expression<Func<TSource, object>> getter, Expression<Func<TTarget, object>> setter)
    {
        var sourceField = getter.Body as MemberExpression;
        var targetField = setter.Body as MemberExpression;

        // here we create new assign expression
        var assign = Expression.Assign(targetField, sourceField);

        // and then compile it with original two parameters
        var lambda = Expression.Lambda<Action<TSource, TTarget>>(assign, getter.Parameters[0], setter.Parameters[0]);
        return lambda.Compile();
    }
}

public class Target
{
    public string TargetName { get; set; }
}

public class NestedSource
{
    public string Dummy { get; set; }
}

public class Source
{
    public NestedSource NestedField { get; set; }
}

更新

因此,每个Lambda表达式都可以具有任何参数。 从代码方面来看,它是ParameterExpression 当将表达式写为典型代码时,则表示函数参数,因此在您的情况下(p) => p.NestedField.Dummy (p)是该函数的参数。 体内的表达式使用它p.NestedField.Dummy ,以便能够对其进行编译p.NestedField.Dummy表达式需要知道该参数。

在这种情况下,您有两个针对目标和源的lambda表达式,它们每个都有自己的参数- (p)(x) ,每个表达式都使用自己的参数。 但是在结果函数中,我们需要同时使用它们,因为函数中有两个参数,因此我们需要将原始ParameterExpression从源和目标重新发送到新的lambda。 或者,您可以创建一个新的ParameterExpression但随后需要创建一棵新树,因为老树将使用旧ParameterExpression 通常,此类操作是通过ExpressionVisitor类完成的,但是在您的情况下,我们可以重新发送原始表达式而无需更改树体。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM