2015-01-22 14 views
8

Ich muss eine Lösung für eine Aufgabe entwerfen, und ich möchte etwas theoretisch ähnlich wie C# ExpressionVisitor verwenden.Was ist die Motivation der Implementierung von C# ExpressionVisitor?

Aus Neugierde öffnete ich die .NET-Quellen für ExpressionVisitor, um es sich anzusehen. Seit dieser Zeit frage ich mich, warum das .NET-Team den Besucher so implementiert hat wie sie.

Zum Beispiel MemberInitExpression.Accept sieht wie folgt aus:

protected internal override Expression Accept(ExpressionVisitor visitor) { 
    return visitor.VisitMemberInit(this); 
} 

My - wahrscheinlich Noob - Frage: Ist es sinnvoll? Ich meine, sollte nicht die Accept-Methode selbst dafür verantwortlich sein, wie sie den Besuch in sich selbst umsetzt? Ich meine ich so etwas wie dies erwartet habe (Entfernen der internal Sichtbarkeit von außen überschreibbar zu sein):

protected override Expression Accept(ExpressionVisitor visitor) { 
    return this.Update(
      visitor.VisitAndConvert(this.NewExpression, "VisitMemberInit"), 
      visitor.Visit(this.Bindings, VisitMemberBinding) 
      ); 
} 

Aber dieser Code ist in der Basis ExpressionVisitor ‚s VisitMemberInit Methode, die von MemberInitExpression.Accept aufgerufen wird. So scheint kein Nutzen der Accept Implementierung hier.

Warum nicht nur den Baum in der Basis verarbeiten ExpressionVisitor, und vergessen Sie alle Accept Methoden?

Ich hoffe, Sie verstehen meine Punkte und hoffen, dass jemand etwas Licht auf die Motivation hinter dieser Implementierung werfen könnte. Vermutlich verstehe ich das Besuchermuster überhaupt nicht? ...

Antwort

2

Ein Besucher kann die Weise außer Kraft setzen, die irgendein Ausdruck besucht wird. Wenn Ihr Vorschlag an allen Orten implementiert wurde, würde der Besucher nie aufgerufen werden. Die gesamte Besuchslogik wäre in den Überschreibungen von Accept enthalten. Nicht-BCL-Code kann diese Methode nicht überschreiben.

Wenn Sie visitor.Visit((Expression)this.SomeExpression) schreiben (wie Sie in der Frage tun), wie werden Sie dann dynamischen Versand auf den Typ SomeExpression durchführen? Jetzt muss der Besucher den dynamischen Versand durchführen. Beachten Sie, dass Ihr zweites Code-Snippet die vereinfachende Annahme macht, dass alle zu durchsuchenden Unter-Ausdrücke einen bekannten Typ haben. Versuchen Sie, den Code für BinaryExpression zu schreiben, um zu sehen, was ich meine.

Vielleicht habe ich etwas nicht verstanden, aber dieser Vorschlag macht keinen Sinn.

Der Zweck der Accept Methode ist eine Leistungsoptimierung. Jedes Akzeptieren ist ein virtueller Anruf, der ziemlich billig ist. Die Alternative wäre, einen großen Schalter im Besucher über den Ausdruckstyp zu haben (der eine Aufzählung ist). Das ist wahrscheinlich langsamer.

+0

Danke. Ich verstehe den Leistungspunkt irgendwie. Wie auch immer, ich habe natürlich in meinem Vorschlag erwähnt, dass die Accept-Methode nicht intern, sondern nur virtuell geschützt ist. Ich werde meine Frage korrigieren, tut mir leid für die Irreführung. –

+1

Das hilft nicht, weil Sie möglicherweise mehrere Besucher verschiedene Dinge tun. Sie können nicht erwarten, dass Benutzercode diese Methode überschreibt. – usr

+0

Wie für Ihre Bearbeitung, dynamisches Display wird jetzt in der ExpressionVisitor.Visit-Methode, in einem großen Schalter ich denke (basierend auf NodeType) gemacht, aber ich muss überprüfen. –

2

Das Besuchermuster ermöglicht es, den Algorithmus im Wesentlichen von der Struktur zu trennen, auf der er arbeitet. In diesem Fall ist die Struktur, auf der es arbeitet, der Ausdrucksbaum.

Beachten Sie, dass die Accept Methode im Besucher virtual ist. Das bedeutet, dass wir verschiedene Implementierungen von ExpressionVisitor schreiben könnten, die verschiedene Dinge zu einem Ausdrucksbaum machen (und tatsächlich gibt es verschiedene Implementierungen). Und wir können dies tun, ohne Code in den Klassen der Ausdrucksbaumstruktur selbst zu ändern.

Beispiele für verschiedene Besucherimplementierungen könnten etwa so aussehen, als hätte ein Besucher den Ausdrucksbaum wieder in eine Zeichenfolge umgewandelt, die C# -Code (oder vielleicht Code in einer anderen Sprache) darstellt.

+0

Ja, ich verstehe das und deshalb habe ich nach der Notwendigkeit der Accpet-Methoden in den Expression-Klassen gefragt. Aber @usr hat darauf hingewiesen, dass es aus Leistungsgründen ist. –

+1

Es geht nicht wirklich um Leistung, aber es geht darum, wo der Code lebt. Er hat Recht mit dem dynamischen Versand, aber das ist nicht der Grund, warum Sie die 'Visit'-Implementierung nicht einfach in die' Accept'-Methode ziehen können. Es gibt zwei Ebenen der dynamischen Verteilung, die hier stattfinden. Die eine besteht darin, abhängig vom Knotentyp, die besucht wird, zu versenden, die zweite besteht darin, an verschiedene Besucherimplementierungen zu senden (ohne dass eine Änderung an den Knotentypen erforderlich ist). – Kyle

+0

Ja, jetzt verstehe ich, dass meine vorgeschlagene Implementierung schlechtes Design ist, weil es den Ausdruck traversal fest codiert. Die andere Frage war nur, warum es Accept-Methoden gibt, anstatt den dynamischen Versand in einem großen Switch in einem ExpressionVisitor-Basisordner durchzuführen. Leider kann ich zwei Antworten nicht akzeptieren, aber trotzdem vielen Dank für Ihre Hinweise. –