2015-01-22 24 views
8

Devo progettare una soluzione per un'attività e vorrei usare qualcosa di teoricamente simile a ExpressionVisitor di C#.Qual è la motivazione dell'implementazione di C# ExpressionVisitor?

Per curiosità ho aperto le fonti .NET per ExpressionVisitor di dare un'occhiata a questo. Da quel momento mi sono chiesto perché il team .NET ha implementato il visitatore come hanno fatto.

Per esempio MemberInitExpression.Accept si presenta così:

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

mio - probabilmente niubbo - domanda è: ha alcun senso? Voglio dire, il metodo Accept non dovrebbe essere responsabile di come implementa la visita all'interno di se stesso? Voglio dire che ho aspettato qualcosa di simile (togliendo la visibilità internal essere superabile dall'esterno):

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

Ma questo è il codice riportato VisitMemberInit metodo di s' base ExpressionVisitor, che viene chiamato da MemberInitExpression.Accept. Quindi sembra che non ci siano vantaggi dell'implementazione qui.

Perché non elaborare solo l'albero nella base ExpressionVisitor e dimenticare tutti i metodi Accept?

Spero che tu capisca i miei punti, e la speranza che qualcuno potesse fare luce sulla motivazione dietro a questa implementazione. Probabilmente non capisco il modello Visitor a tutti? ...

risposta

2

Un visitatore può ignorare il modo in cui ogni espressione viene visitato. Se la tua proposta è stata implementata in tutti i luoghi, il visitatore non verrebbe mai chiamato. Tutta la logica di visita sarebbe negli overrides di Accept. Il codice non BCL non può sovrascrivere questo metodo.

Se si scrive visitor.Visit((Expression)this.SomeExpression) (come si fa nella domanda), allora come si intende eseguire la spedizione dinamica sul tipo di SomeExpression? Ora il visitatore deve eseguire la spedizione dinamica. Nota che il tuo secondo snippet di codice semplifica l'ipotesi che tutte le sottoespressioni da visitare abbiano un tipo noto. Prova a scrivere il codice per BinaryExpression per vedere cosa intendo.

Forse non ho capito qualcosa ma questa proposta non ha senso.

Lo scopo del metodo Accept è un'ottimizzazione delle prestazioni. Ogni accetta è una chiamata virtuale che è piuttosto economica. L'alternativa sarebbe avere un enorme cambiamento nel visitatore rispetto al tipo di espressione (che è un enum). Probabilmente è più lento.

+0

Grazie. Capisco il punto di prestazione. Comunque, naturalmente, nella mia proposta, penso che il metodo Accept non sia interno, ma solo virtuale protetto. Correggerò la mia domanda, mi dispiace per averlo fuorviato. –

+1

Questo non aiuta perché potresti avere più visitatori che fanno cose diverse. Non puoi aspettarti che il codice utente sostituisca questo metodo. – usr

+0

Come per la tua modifica, il displatch dinamico è fatto proprio ora nel metodo ExpressionVisitor.Visit, in un enorme switch credo (basato su NodeType), ma devo controllare. –

2

Il modello visitatore permette l'algoritmo essenzialmente essere separato dalla struttura è operante su. In questo caso la struttura su cui opera è l'albero delle espressioni.

Si noti che il metodo Accept nel visitatore è virtual. Ciò significa che potremmo in teoria scrivere diverse implementazioni di ExpressionVisitor che fanno cose diverse a un albero di espressioni (e, in effetti, esistono diverse implementazioni). E possiamo farlo senza modificare alcun codice nelle stesse classi di espressioni.

Esempi di diverse implementazioni visitatore potrebbe essere qualcosa come avere un visitatore che trasforma l'albero di espressione in una stringa che rappresenta il codice C# (o forse il codice in un'altra lingua).

+0

Sì, lo capisco, ed è per questo che mi chiedevo della necessità dei metodi Accpet nelle classi di espressioni. Ma @usr ha sottolineato che è dovuto a motivi di prestazioni. –

+1

Tuttavia, non è proprio per le prestazioni, si tratta di dove vive il codice. Ha ragione riguardo alla spedizione dinamica, ma non è questo il motivo per cui non puoi semplicemente attuare direttamente l'implementazione di 'Visit' nel metodo' Accept'. Qui ci sono due livelli di invio dinamico. Uno è di inviare a seconda del tipo di nodo visitato, ma il secondo è quello di inviare a diverse implementazioni di visitatori (senza richiedere una modifica ai tipi di nodo). – Kyle

+0

Sì, ora capisco che l'implementazione proposta è di progettazione errata perché codifica l'espressione traversal. L'altra domanda riguardava solo il motivo per cui esistono metodi Accept invece di eseguire la distribuzione dinamica in un enorme switch in un ExpressionVisitor di base. Purtroppo non posso accettare due risposte, ma grazie comunque per i tuoi suggerimenti. –

Problemi correlati