有可能对表达式树做些什么。。。有三种方法:为每个属性创建一个表达式,为每个属性组合创建一个表达式,为每个包含大
foreach (var property in properties) switch (property) { case "Prop1": target.Prop1 = source.Prop1; break; ... } }
(在这种情况下
target.Prop1 = source.Prop1
不使用反射)。
我会做第一个最简单的。
public static class Tools
{
public static void CopyFrom<T>(this T target, T source, params string[] properties)
{
ToolsImpl<T>.CopyFrom(target, source, properties);
}
private static class ToolsImpl<T>
{
private static readonly ConcurrentDictionary<string, Action<T, T>> delegates = new ConcurrentDictionary<string, Action<T, T>>();
public static void CopyFrom(T target, T source, string[] properties)
{
foreach (var property in properties)
{
Action<T, T> del;
if (!delegates.TryGetValue(property, out del))
{
var t2 = Expression.Parameter(typeof(T), "t");
var s2 = Expression.Parameter(typeof(T), "s");
var prop = typeof(T).GetProperty(property);
// The ?. in the source: skip missing properties
if (prop == null)
{
continue;
}
Expression<Action<T, T>> exp = Expression.Lambda<Action<T, T>>(Expression.Assign(Expression.Property(t2, prop), Expression.Property(s2, prop)), t2, s2);
del = exp.Compile();
delegates.TryAdd(property, del);
}
del(target, source);
}
}
}
}
这里是“one property==one expression tree”的代码。为“一组属性=一个表达式树”执行此操作稍微复杂一些,因为您需要一个
string[]
. 第三个更复杂(你有
for
/
foreach
它不在表达式树中,因此必须构建,您有
switch
那总是一种痛苦)
和(编译的)表达式树一样,第一次运行的速度和狗一样慢,然后它变得更快。
附录
只是出于好奇,第三种方式(模拟
对于
循环+
转换
) :
public static class Tools
{
public static void CopyFrom<T>(this T target, T source, params string[] properties)
{
ToolsImpl<T>.CopyTo(source, target, properties);
}
private static class ToolsImpl<T>
{
public static readonly Action<T, T, string[]> CopyTo;
static ToolsImpl()
{
var source = Expression.Parameter(typeof(T), "s");
var target = Expression.Parameter(typeof(T), "t");
var properties = Expression.Parameter(typeof(string[]), "properties");
// indexer of the for cycle
var i = Expression.Variable(typeof(int), "i");
// case "prop1": target.prop1 = source.prop1
var cases = typeof(T)
.GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(x => x.CanRead && x.CanWrite && x.GetIndexParameters().Length == 0)
.Select(x => Expression.SwitchCase(Expression.Assign(Expression.Property(target, x), Expression.Property(source, x)), Expression.Constant(x.Name)));
// switch properties[i]:
var sw = Expression.Switch(typeof(void), Expression.ArrayAccess(properties, i), null, null, cases);
var lblForBegin = Expression.Label(typeof(void), "for begin");
var lblForCheck = Expression.Label(typeof(void), "for check");
// we simulate a for (int i = 0; i < properties.Length; ++i
var body = Expression.Block(new[] { i },
new Expression[]
{
Expression.Assign(i, Expression.Constant(0)), // ix = 0
Expression.Goto(lblForCheck), // goto lblForCheck
Expression.Label(lblForBegin), // :lblForBegin
sw, // switch ()
Expression.PreIncrementAssign(i), // ++i
Expression.Label(lblForCheck), // :lblForCheck
Expression.IfThen(Expression.LessThan(i, Expression.ArrayLength(properties)), Expression.Goto(lblForBegin)), // if ix < properties.Length goto lblForBegin
});
var exp = Expression.Lambda<Action<T, T, string[]>>(body, source, target, properties);
CopyTo = exp.Compile();
}
}
}