Leichter Besuchen mit Lambdas

Ich verbessere gerade mein C# und schaue daher eine Menge YouTube-Videos. Eine schöne Methode einen Visitor zu implementieren habe ich in Using C#’s Type System Effectively gefunden: Die Funktionen für die verschiedenen Knotentypen werden einfach als Lambdas übergeben.

Grundlage ist beispielsweise folgende Klassenstruktur:

public abstract class Expression
{
    public abstract T Visit<T>(
        Func<bool, T> literal,
        Func<Expression, Expression, T> and);
}
 
public class AndExpression : Expression
{
    public AndExpression(Expression left, Expression right)
    {
        Left = left;
        Right = right;
    }
 
    public Expression Left { get; }
    public Expression Right { get; }
    public override T Visit<T>(
        Func<bool, T> literal,
        Func<Expression, Expression, T> and) => and(Left, Right);
    }
}
 
public class LiteralExpression : Expression
{
    public LiteralExpression(bool value)
    {
        Value = value;
    }
 
    public bool Value { get; }
 
    public override T Visit<T>(
        Func<bool, T> literal,
        Func<Expression, Expression, T> and) => literal(Value);
    }
}

Jetzt kann ich mit folgender Funktion den Ausdruck in einen String umwandeln:

static string ToString(Expression expr) => expr.Visit<string>(
    literal: (value) => value ? "TRUE" : "FALSE",
    and: (left, right) => ToString(left) + " and " + ToString(right)
);

Oder ganz einfach auswerten:

static bool Evaluate(Expression expr) => expr.Visit<bool>(
    literal: (value) => value,
    and: (left, right) => Evaluate(left) && Evaluate(right)
);

Mir gefällt die Lösung sehr, da sie typsicher ist (wenn ich einen Knotentyp hinzufüge, muss ich Visit überall anpassen) und einfache Transformationen einfach in einer Funktion geschrieben können.

Definitiv besser als Java. 🙂

Update

Es sind natürlich nicht die Lambda-Ausdrücke, die das so möglich machen sondern die benannten Argumente. Sorry. Trotzdem besser als Java.