2010-02-09 15 views
5

Ok, sto cercando di fare quanto segue:Metodo sovraccarico: imbuto chiamata a sovraccarico argomento classe derivata

 protected bool ValidAdvert(Base item) 
     { 
      throw ThisIsAnAbstractClassException(); 
     } 

     protected bool ValidAdvert(Derived1 item) 
     { 
      return ADerived1SpecificPredicate; 
     } 

     protected bool ValidAdvert(Derived2 item) 
     { 
      return ADerived2SpecificPredicate; 
     }

e hanno le versioni classe derivata del metodo chiamato, quando una classe base viene passata al il metodo. La classe base è astratta quindi dovrebbe essere teoricamente possibile?

Prima che qualcuno dica qualcosa sull'overloading del metodo sulle classi stesse, la logica all'interno dei metodi si basa su un gran numero di condizioni diverse, nessuna delle quali è correlata, e nessuna delle quali correlata direttamente alla classe Base/Derivata (come ad esempio lo stato di accesso, etc.)

risposta

6

Se il doppio invio è troppo invadente, è possibile richiamare il metodo mediante la riflessione e risolvere il sovraccarico corretto in modo dinamico.

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    Type type = typeof(CurrentClass); 

    MethodInfo method = type.GetMethod("ValidAdvert", 
             BindingFlags.Instance | BindingFlags.NonPublic, 
             null, 
             new Type[] { item.GetType() }, 
             null); 
    return (bool)method.Invoke(this, new object[] { item }); 
} 

protected bool ValidAdvert(Derived1 item) 
{ 
    return ADerived1SpecificPredicate; 
} 

protected bool ValidAdvert(Derived2 item) 
{ 
    return ADerived2SpecificPredicate; 
} 

Questo modello è chiamato MultipleDispatch, mentre chiamando un metodo per riflessione è più lenta di chiamare il metodo direttamente (non sarà un overhead se il metodo viene chiamato a meno di qualche centinaio di volte al secondo), ha il vantaggio di non richiedere di modificare la gerarchia di classi come nel modello di Double Dispatch.

Questo sarà ancora più facile quando C# 4.0 se ne esce con la parola chiave dinamica:

protected bool ValidAdvert(Base item) 
{ 
    if (item.GetType() == typeof(Base)) 
     throw new ThisIsAnAbstractClassException(); 

    dynamic dynamicThis = this; 

    return (bool)dynamicThis.ValidAdvert(item as dynamic); 
} 
+0

Anche intelligente, ed evita di fare scherzi con le varie classi derivate (vorrei che ce ne fossero solo 2!). –

+0

piuttosto note: è necessario eseguire il cast per il ritorno, e method.Invoke prende un [] di oggetti, e così craps out se si tenta di passare solo il 1 :) Grazie però! –

+0

@Ed - Penso che mantenere il predicato con la classe derivata sia in realtà il metodo preferito. Memorizzare la logica specifica della classe derivata in una classe base crea un accoppiamento inverso tra i due. Non la vedo come una cosa buona. La classe base non dovrebbe aver bisogno di sapere nulla delle sue classi derivate. – tvanfosson

5

un luogo perfetto per utilizzare double dispatch:

class Base 
{ 
    public abstract bool IsValid(IAdvertValidator validator); 
} 

class Derived1 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

class Derived2 : Base 
{ 
    public bool IsValid(IAdvertValidator validator) 
    { 
     return validator.ValidAdvert(this); 
    } 
} 

interface IAdvertValidator 
{ 
    bool ValidAdvert(Derived1 d1); 
    bool ValidAdvert(Derived2 d2); 
} 
+0

intelligente, mi piace. –

+0

Un uso ragionevole del modello. Un'alternativa sarebbe quella di esporre i metodi di convalida dal validatore e utilizzarli all'interno di un'implementazione di IsValid, mantenendo il flusso di controllo effettivo di controllo nella classe. – tvanfosson

0

io non sono sicuro esattamente come si potrà mai avere un'istanza della classe base che non è in realtà un'istanza di una classe derivata. Poiché la classe base è astratta, non può essere istanziata. Suppongo che potrebbe corrispondere a qualsiasi classe derivata che non ha una firma specifica, ma non sembra essere quello che stai cercando.

+0

Sì, è sempre una delle classi derivate, tuttavia è anche tecnicamente la classe base, ovvero il metodo della classe base viene chiamato quando faccio (Elenco ) i.Where (ValidateAdvert), ad esempio. –

+0

Ah - Stavo leggendo il tuo esempio di codice, non la tua domanda. In realtà non vuoi che faccia un'eccezione, vuoi invece richiamare uno degli altri metodi. In tal caso, andrei con il suggerimento di @ Anton, o qualcosa di simile. Si noti che è possibile fornire il validatore in un costruttore in modo che non sia necessario passarlo come argomento. Ho anche usato una classe di convalida statica che convalida le proprietà semplici rimuovendo la necessità di mantenere un riferimento ad esso. Devi stare attento con questi, però, poiché possono rendere più difficili i test se non stai attento. – tvanfosson

+0

Il validatore è molto una classe specifica per pagina e stato, e quindi non funzionerebbe bene come statico, tuttavia penso che sarebbe andato abbastanza bene nel metodo di riflessione, è un peccato che questo non sia supportato di default anche se! –

Problemi correlati