Я хочу встроить дерево выражений, например
Expression<Func<MyObject, double>> expr = (o) => o.Value;
в большее дерево выражений, генерируемое парсером. Однако параметр o уже определен внутри внешнего дерева выражений. В принципе мне пришлось бы искать тело expr
и заменять все вхождения параметра экземпляром из дерева синтаксического анализа.
Есть ли встроенный способ сделать это? Или существует даже способ прямого генерации лямбда-выражения при указании экземпляра параметра заранее?
Вы не можете напрямую поручить компилятору повторно использовать существующие экземпляры ParameterExpression
, но после этого вы можете их заменить (фактически создавая новые деревья выражений).
Встроенный ExpressionVisitor
помогает при тяжелом подъеме; это не-посетитель, который вы получаете, чтобы добавить необходимые функции. В этом случае вам нужно указать ему заменить экземпляры ParameterExpression
, чтобы вы могли:
// Sorry for the atrocious formatting, wanted to keep it scrollbar-free
class ParameterReplacementVisitor : ExpressionVisitor
{
private readonly
IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>>
replacementMap;
public ParameterReplacementVisitor(
IEnumerable<KeyValuePair<ParameterExpression, ParameterExpression>> map)
{
this.replacementMap = map;
}
protected override Expression VisitLambda<T>(Expression<T> node)
{
return Expression.Lambda<T>(
Visit(node.Body),
node.Parameters.Select(Visit).Cast<ParameterExpression>());
}
protected override Expression VisitParameter(ParameterExpression node)
{
var replacement = this.replacementMap
.Where(p => p.Key == node)
.DefaultIfEmpty()
.First().Value;
return base.VisitParameter(replacement ?? node);
}
}
которые вы можете использовать следующим образом:
Expression<Func<int, bool>> e1 = i => true;
Expression<Func<int, bool>> e2 = j => false;
Console.WriteLine(e1.Parameters[0] == e2.Parameters[0]); // false
var replacements = new Dictionary<ParameterExpression, ParameterExpression>
{
{ e1.Parameters[0], e2.Parameters[0] }
};
var replacer = new ParameterReplacementVisitor(replacements);
var e3 = replacer.VisitAndConvert(e1, "replacing parameters");
Console.WriteLine(e3.Parameters[0] == e2.Parameters[0]); // true