Поделиться через


Практическое руководство. Изменение деревьев выражений

Обновлен: Ноябрь 2007

В этом разделе показано, как изменить дерево выражения. Деревья выражений являются неизменяемыми, что означает невозможность их изменения напрямую. Чтобы изменить дерево выражения, необходимо создать копию существующего дерева выражения и после создания копии внести необходимые изменения. Можно использовать обход дерева выражения для просмотра существующего дерева выражения и копирования каждого пройденного узла.

Чтобы изменить дерево выражения

  1. В Visual Studio создайте новый проект Консольное приложение.

  2. Добавьте ссылку на System.Core.dll, если она отсутствует.

  3. Добавьте в проект класс ExpressionVisitor.

    Этот код доступен в Практическое руководство. Реализация обхода дерева выражения.

    Добавьте директивы using (в Visual Basic операторы Imports) в файл для следующих пространств имен: System.Collections.Generic, System.Collections.ObjectModel и System.Linq.Expressions.

  4. Добавьте в проект класс AndAlsoModifier.

    Этот класс наследует из класса ExpressionVisitor и специально предназначен для изменения выражений, которые представляют условные операции AND. Он изменяет эти операции с условного AND на условное OR. Для этого класс переопределяет метод VisitBinary базового типа, потому что условные выражения AND представлены как двоичные выражения. Если выражение, переданное в метод VisitBinary, представляет условную операцию AND, код создает новое выражение, которое содержит условный оператор OR вместо условного оператора AND. Если выражение, передаваемое в VisitBinary, не представляет условную операцию AND, метод передает выполнение реализации базового класса. Методы базового класса создают узлы, которые похожи на переданные деревья выражений, однако поддеревья этих деревьев заменены на деревья выражений, которые были рекурсивно созданы при обходе.

    Public Class AndAlsoModifier
        Inherits ExpressionVisitor
    
        Public Function Modify(ByVal expr As Expression) As Expression
            Return Visit(expr)
        End Function
    
        Protected Overrides Function VisitBinary(ByVal b As BinaryExpression) As Expression
            If b.NodeType = ExpressionType.AndAlso Then
                Dim left = Me.Visit(b.Left)
                Dim right = Me.Visit(b.Right)
    
                ' Make this binary expression an OrElse operation instead 
                ' of an AndAlso operation.
                Return Expression.MakeBinary(ExpressionType.OrElse, left, right, _
                                             b.IsLiftedToNull, b.Method)
            End If
    
            Return MyBase.VisitBinary(b)
        End Function
    End Class
    
    public class AndAlsoModifier : ExpressionVisitor
    {
        public Expression Modify(Expression expression)
        {
            return Visit(expression);
        }
    
        protected override Expression VisitBinary(BinaryExpression b)
        {
            if (b.NodeType == ExpressionType.AndAlso)
            {
                Expression left = this.Visit(b.Left);
                Expression right = this.Visit(b.Right);
    
                // Make this binary expression an OrElse operation instead of an AndAlso operation.
                return Expression.MakeBinary(ExpressionType.OrElse, left, right, b.IsLiftedToNull, b.Method);
            }
    
            return base.VisitBinary(b);
        }
    }
    

    Добавьте директиву using (или оператор Imports в Visual Basic) в файл для пространства имен System.Linq.Expressions.

  5. Добавьте код в метод Main в файле Program.cs (Module1.vb в Visual Basic) для создания дерева выражения и передачи его в метод, который изменит его.

    В следующем коде создается выражение, которое содержит условную операцию AND. Затем в нем создается экземпляр класса AndAlsoModifier и в метод Modify этого класса передается выражение. Как исходное, так и измененное дерево выражения выводятся для отображения изменений.

    Dim expr As Expression(Of Func(Of String, Boolean)) = _
        Function(name) name.Length > 10 AndAlso name.StartsWith("G")
    
    Console.WriteLine(expr)
    
    Dim modifier As New AndAlsoModifier()
    Dim modifiedExpr = modifier.Modify(CType(expr, Expression))
    
    Console.WriteLine(modifiedExpr)
    
    ' This code produces the following output:
    ' name => ((name.Length > 10) && name.StartsWith("G"))
    ' name => ((name.Length > 10) || name.StartsWith("G"))
    
    
    Expression<Func<string, bool>> expr = name => name.Length > 10 && name.StartsWith("G");
    Console.WriteLine(expr);
    
    AndAlsoModifier treeModifier = new AndAlsoModifier();
    Expression modifiedExpr = treeModifier.Modify((Expression) expr);
    
    Console.WriteLine(modifiedExpr);
    
    /*  This code produces the following output:
    
        name => ((name.Length > 10) && name.StartsWith("G"))
        name => ((name.Length > 10) || name.StartsWith("G"))
    */
    
    

    Добавьте директиву using (или оператор Imports в Visual Basic) в файл для пространства имен System.Linq.Expressions.

  6. Скомпилируйте и запустите приложение.

См. также

Задачи

Практическое руководство. Выполнение деревьев выражений

Пошаговое руководство. Создание поставщика IQueryable LINQ

Практическое руководство. Реализация обхода дерева выражения

Основные понятия

Деревья выражений