Ist es möglich, einen C # -Ausdrucksbaum so zu interpretieren, dass er JavaScript ausgibt?

c# expression-trees javascript transformation

Frage

Zum Beispiel, wenn Sie einen Ausdruck wie folgt haben:

Expression<Func<int, int>> fn = x => x * x;

Gibt es etwas, das den Ausdrucksbaum durchqueren und erzeugen wird?

"function(x) { return x * x; }"

Akzeptierte Antwort

Es ist wahrscheinlich nicht einfach, aber ja, es ist absolut machbar. ORMs wie Entity Framework oder Linq to SQL tun dies, um Linq-Abfragen in SQL zu übersetzen, aber Sie können tatsächlich alles, was Sie wollen, aus dem Ausdrucksbaum erzeugen ...

Sie sollten einen ExpressionVisitor implementieren, um den Ausdruck zu analysieren und zu transformieren.


EDIT: Hier ist eine sehr einfache Implementierung, die für Ihr Beispiel funktioniert:

Expression<Func<int, int>> fn = x => x * x;
var visitor = new JsExpressionVisitor();
visitor.Visit(fn);
Console.WriteLine(visitor.JavaScriptCode);

...

class JsExpressionVisitor : ExpressionVisitor
{
    private readonly StringBuilder _builder;

    public JsExpressionVisitor()
    {
        _builder = new StringBuilder();
    }

    public string JavaScriptCode
    {
        get { return _builder.ToString(); }
    }

    public override Expression Visit(Expression node)
    {
        _builder.Clear();
        return base.Visit(node);
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        _builder.Append(node.Name);
        base.VisitParameter(node);
        return node;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        base.Visit(node.Left);
        _builder.Append(GetOperator(node.NodeType));
        base.Visit(node.Right);
        return node;
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        _builder.Append("function(");
        for (int i = 0; i < node.Parameters.Count; i++)
        {
            if (i > 0)
                _builder.Append(", ");
            _builder.Append(node.Parameters[i].Name);
        }
        _builder.Append(") {");
        if (node.Body.Type != typeof(void))
        {
            _builder.Append("return ");
        }
        base.Visit(node.Body);
        _builder.Append("; }");
        return node;
    }

    private static string GetOperator(ExpressionType nodeType)
    {
        switch (nodeType)
        {
            case ExpressionType.Add:
                return " + ";
            case ExpressionType.Multiply:
                return " * ";
            case ExpressionType.Subtract:
                return " - ";
            case ExpressionType.Divide:
                return " / ";
            case ExpressionType.Assign:
                return " = ";
            case ExpressionType.Equal:
                return " == ";
            case ExpressionType.NotEqual:
                return " != ";

            // TODO: Add other operators...
        }
        throw new NotImplementedException("Operator not implemented");
    }
}

Es verarbeitet nur Lambdas mit einer einzigen Anweisung, aber der C # -Compiler kann keinen Ausdrucksbaum für einen Block Lambda generieren.

Es gibt natürlich noch viel zu tun, das ist eine sehr minimale Implementierung ... Sie müssen wahrscheinlich Methodenaufrufe ( VisitMethodCall ), Eigenschaften- und VisitMember ( VisitMember ) usw. VisitMember .


Beliebte Antwort

Skript # wird von internen Entwicklern von Microsoft verwendet, um genau dies zu tun.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow