所以实际进行映射的方法不是that很难,但遗憾的是我没有找到一个很好的方法来概括它。这是一个方法,需要一个Func<T1, TResult>
并将其映射到一个委托,其中参数比T1
:
public static Expression<Func<NewParam, TResult>> Foo<NewParam, OldParam, TResult>(
Expression<Func<OldParam, TResult>> expression)
where NewParam : OldParam
{
var param = Expression.Parameter(typeof(NewParam));
return Expression.Lambda<Func<NewParam, TResult>>(
expression.Body.Replace(expression.Parameters[0], param)
, param);
}
这使用了Replace
方法将一个表达式的所有实例替换为另一个表达式的所有实例。定义是:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
现在我们可以使用这个方法(应该给它一个更好的名字),如下所示:
Expression<Func<object, bool>> oldExpression = whatever;
Expression<Func<string, bool>> newExpression =
Foo<string, object, bool>(oldExpression);
当然,自从Func
实际上就其参数而言是协变的,我们可以确定对此方法的任何调用都会生成不会添加运行时故障点的表达式。
你可以简单地制作这个版本Func<T1, T2, TResult>
,依此类推,直到 16 种不同类型Func
如果您愿意,只需为每个参数创建一个参数表达式,然后用新的替换所有旧的。这会很乏味,但只要遵循模式即可。鉴于新旧参数类型都需要有一个通用参数,并且无法推断参数,这会变得……混乱。