[英]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"
我玩了一些您的代碼,我想我可以給您一個不錯的流利風格的字段地圖生成器。 給定您的Foo
& Bar
類,您可以運行以下代碼:
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.