Практическое руководство. Изменение деревьев выражений (C# и Visual Basic)

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

Примечание

Исходный код для класса ExpressionVisitor можно найти на веб-сайте CodePlex.

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

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

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

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

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

    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);
        }
    }
    
  3. Добавьте код в метод Main в файле Program.cs (Module1.vb в Visual Basic) для создания дерева выражения и передачи его в метод, который изменит его.

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

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

            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"))
                */
    
    
  4. Скомпилируйте и запустите приложение.

См. также

Задачи

Практическое руководство. Выполнение деревьев выражений (C# и Visual Basic)

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

Деревья выражений (C# и Visual Basic)