簡體   English   中英

如何將轉換函數插入表達式樹?

[英]How do I insert a conversion function into my expression tree?

這是表達樹中的學習練習。

我有這個工作代碼:

class Foo
{
    public int A { get; set; }
    public string B { get; set; }
}

class Bar
{
    public int C { get; set;}
    public string D { get; set; }
}

class FieldMap
{
    public PropertyInfo From { get; set; }
    public PropertyInfo To { get; set; }

}

class Program
{
    static Action<TFrom, TTo> CreateMapper<TFrom, TTo>(IEnumerable<FieldMap> fields)
    {
        ParameterExpression fromParm = Expression.Parameter(typeof(TFrom), "from");
        ParameterExpression toParm = Expression.Parameter(typeof(TTo), "to");

        //var test = new Func<string, string>(x => x);
        //var conversionExpression = Expression.Call(null, test.Method);

        var assignments = from fm in fields
                            let fromProp = Expression.Property(fromParm, fm.From)
                            let toProp = Expression.Property(toParm, fm.To)
                            select Expression.Assign(toProp, fromProp);

        var lambda = Expression.Lambda<Action<TFrom, TTo>>(
            Expression.Block(assignments), 
            new ParameterExpression[] { fromParm, toParm });

        return lambda.Compile();
    }

    static void Main(string[] args)
    {
        var pa = typeof(Foo).GetProperty("A");
        var pb = typeof(Foo).GetProperty("B");
        var pc = typeof(Bar).GetProperty("C");
        var pd = typeof(Bar).GetProperty("D");

        var mapper = CreateMapper<Foo, Bar>(new FieldMap[] 
        { 
            new FieldMap() { From = pa, To = pc }, 
            new FieldMap() { From = pb, To = pd } 
        });

        Foo f = new Foo();
        Bar b = new Bar();

        f.A = 20;
        f.B = "jason";
        b.C = 25;
        b.D = "matt";

        mapper(f, b);     // copies properties from f into b
    }
}

效果很好。 如前所述,它將相應的屬性從f復制到b 現在,假設我想添加一些轉換或格式化方法,該方法采用“ from property”,做一些魔術,然后將“ to property”設置為等於結果。 注意在CreateMapper中間的兩條注釋行。

我該如何完成? 我已經走了這么遠,但是我現在有點迷路了。

您的代碼示例幾乎就在那里。 您可以按照顯然要使用的方式使用Expression.Call進行轉換。 除了將toProp分配給toProp fromProp 之外 ,您還可以分配一個表示轉換值的MethodCallExpression

這里最棘手的部分是弄清楚如何進行轉換,我認為該轉換因不同的屬性而異。

您可以將LINQ表達式替換為:

var assignments = from fm in fields
                  let fromProp = Expression.Property(fromParm, fm.From)
                  let fromPropType = fm.From.PropertyType
                  let fromTransformed 
                       = Expression.Call(GetTransform(fromPropType), fromProp)
                  let toProp = Expression.Property(toParm, fm.To)
                  select Expression.Assign(toProp, fromTransformed);

(注意,賦值的右側現在是fromTransformed而不是fromProp 。)

GetTransform看起來像這樣(我在這里假設轉換的性質僅取決於屬性的類型 ):

private static MethodInfo GetTransform(Type type)
{
    return typeof(Program).GetMethod(GetTransformName(type));
}

private static string GetTransformName(Type type)
{
    if (type == typeof(int))
        return "MapInt";

    if (type == typeof(string))
        return "MapString";

    throw new ArgumentException("Unknown type");
}

然后剩下的唯一要做的就是自己填寫轉換。 例如:

public static int MapInt(int x)  { return x * 2; }

public static string MapString(string x)  { return x + x;  }

然后,您的用法測試方法將產生:

b.c == 40
b.d == "jasonjason"

我玩了一些您的代碼,我想我可以給您一個不錯的流利風格的字段地圖生成器。 給定您的FooBar類,您可以運行以下代碼:

var foo = new Foo() { A = 20, B = "jason", };
var bar = new Bar() { C = 25, D = "matt", };

var fm = new FieldMapBuilder<Foo, Bar>()
    .AddMap(f => f.A, b => b.C)
    .AddMap(f => f.B, b => b.D)
    .AddMap(f => f.A, b => b.D, x => String.Format("!{0}!", x))
    .Compile();

fm(foo, bar);

結果是, bar現在看起來像這樣聲明:

var bar = new Bar() { C = 20, D = "!20!", };

關於此代碼的好處是,您無需在調用代碼中進行任何反射,可以推斷屬性類型,並且它可以巧妙地處理不同類型的映射屬性。

這是執行此操作的代碼:

public class FieldMapBuilder<TFrom, TTo>
{
    private Expression<Action<TFrom, TTo>>[] _fieldMaps = null;

    public FieldMapBuilder()
    {
        _fieldMaps = new Expression<Action<TFrom, TTo>>[] { };
    }

    public FieldMapBuilder(Expression<Action<TFrom, TTo>>[] fieldMaps)
    {
        _fieldMaps = fieldMaps;
    }

    public FieldMapBuilder<TFrom, TTo> AddMap<P>(
        Expression<Func<TFrom, P>> source,
        Expression<Func<TTo, P>> destination)
    {
        return this.AddMap<P, P>(source, destination, x => x);
    }

    public FieldMapBuilder<TFrom, TTo> AddMap<PFrom, PTo>(
        Expression<Func<TFrom, PFrom>> source,
        Expression<Func<TTo, PTo>> destination,
        Expression<Func<PFrom, PTo>> map)
    {
        var paramFrom = Expression.Parameter(typeof(TFrom), "from");
        var paramTo = Expression.Parameter(typeof(TTo), "to");

        var invokeExpressionFrom =
                Expression.Invoke(map, Expression.Invoke(source, paramFrom));

        var propertyExpressionTo =
                Expression.Property(paramTo,
            (destination.Body as MemberExpression).Member as PropertyInfo);

        var assignmentExpression =
                Expression.Assign(propertyExpressionTo, invokeExpressionFrom);

        return new FieldMapBuilder<TFrom, TTo>(
                _fieldMaps.Concat(new Expression<Action<TFrom, TTo>>[]
                {
                    Expression.Lambda<Action<TFrom, TTo>>(
                        assignmentExpression,
                        paramFrom,
                        paramTo)
                }).ToArray());
    }

    public Action<TFrom, TTo> Compile()
    {
        var paramFrom = Expression.Parameter(typeof(TFrom), "from");
        var paramTo = Expression.Parameter(typeof(TTo), "to");

        var expressionBlock =
            Expression.Block(_fieldMaps
                .Select(fm => Expression.Invoke(fm, paramFrom, paramTo))
                .ToArray());

        var lambda = Expression.Lambda<Action<TFrom, TTo>>(
            expressionBlock, 
            paramFrom,
            paramTo);

        return lambda.Compile();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM