为了做到这一点,您需要完全重建表达式树;参数需要重新映射,并且所有现在与不同类型交互的成员访问都需要重新应用。幸运的是,
ExpressionVisitor
类可以使这些工作更加容易;例如(在一般情况下完成所有工作,而不仅仅是
Func<T,bool>
谓词使用):
class TypeConversionVisitor : ExpressionVisitor
{
private readonly Dictionary<Expression, Expression> parameterMap;
public TypeConversionVisitor(
Dictionary<Expression, Expression> parameterMap)
{
this.parameterMap = parameterMap;
}
protected override Expression VisitParameter(ParameterExpression node)
{
Expression found;
if(!parameterMap.TryGetValue(node, out found))
found = base.VisitParameter(node);
return found;
}
protected override Expression VisitMember(MemberExpression node)
{
var expr = Visit(node.Expression);
if (expr.Type != node.Type)
{
MemberInfo newMember = expr.Type.GetMember(node.Member.Name)
.Single();
return Expression.MakeMemberAccess(expr, newMember);
}
return base.VisitMember(node);
}
}
在这里,我们传入一个参数字典来重新映射,并将其应用于
VisitParameter
中。此外,在
VisitMember
中,我们还检查是否已经切换了类型(如果
Visit
涉及
ParameterExpression
或另一个
MemberExpression
,则可以在任何时候发生):如果是,则尝试找到同名的另一个成员。
接下来,我们需要一个通用的lambda转换重写方法:
private static Expression<TTo> ConvertImpl<TFrom, TTo>(Expression<TFrom> from)
where TFrom : class
where TTo : class
{
var fromTypes = from.Type.GetGenericArguments();
var toTypes = typeof(TTo).GetGenericArguments();
if (fromTypes.Length != toTypes.Length)
throw new NotSupportedException(
"Incompatible lambda function-type signatures");
Dictionary<Type, Type> typeMap = new Dictionary<Type,Type>();
for (int i = 0; i < fromTypes.Length; i++)
{
if (fromTypes[i] != toTypes[i])
typeMap[fromTypes[i]] = toTypes[i];
}
Dictionary<Expression, Expression> parameterMap
= new Dictionary<Expression, Expression>();
ParameterExpression[] newParams =
new ParameterExpression[from.Parameters.Count];
for (int i = 0; i < newParams.Length; i++)
{
Type newType;
if(typeMap.TryGetValue(from.Parameters[i].Type, out newType))
{
parameterMap[from.Parameters[i]] = newParams[i] =
Expression.Parameter(newType, from.Parameters[i].Name);
}
else
{
newParams[i] = from.Parameters[i];
}
}
var body = new TypeConversionVisitor(parameterMap).Visit(from.Body);
return Expression.Lambda<TTo>(body, newParams);
}
这个方法接受一个任意的
Expression<TFrom>
和一个
TTo
,通过以下步骤将其转换为
Expression<TTo>
:
- 找出
TFrom
/ TTo
之间不同的类型
- 使用它来重新映射参数
- 使用我们刚刚创建的表达式访问器
- 最后构造所需签名的新lambda表达式
然后,将所有内容组合起来并公开我们的扩展方法。
public static class Helpers {
public static Expression<Func<TTo, bool>> Convert<TFrom, TTo>(
this Expression<Func<TFrom, bool>> from)
{
return ConvertImpl<Func<TFrom, bool>, Func<TTo, bool>>(from);
}
}
et voila; 一个通用的 lambda 转换程序,具有特定实现:
Expression<Func<Test, bool>> fc2 = fc1.Convert<TestDTO, Test>()