这是表达树的学习练习。
我有这个工作代码:
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
中间的两条注释掉的行。
我如何做到这一点?我到目前为止,但现在我有点迷失了。
答案 0 :(得分:3)
您的代码示例几乎就在那里;您可以使用Expression.Call
进行转换,因为您正在尝试这样做。您可以分配到表示转换价值的 MethodCallExpression ,而不是将toProp
分配给fromProp
MemberExpression 。
这里棘手的部分是找出如何进行转换,我认为这对于不同的属性会有所不同。
您可以将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"
答案 1 :(得分:2)
我对你的代码有点玩,我想我可以给你一个很好的流利风格的字段地图构建器。鉴于您的课程Foo
&amp; 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();
}
}