2010-07-25 9 views
5

Considerate accettabile o una cattiva pratica creare una classe generica astratta che prende come parametro di tipo una classe che deriva da se stessa?Classi generiche astratte che assumono parametri di tipo derivati ​​da quella classe

Ciò consente alla classe generica astratta di manipolare le istanze della classe derivata e in particolare la possibilità di creare nuove() istanze della classe derivata come necessario e può aiutare a evitare la ripetizione del codice nelle classi concrete che ne derivano.

Se "cattivo" quale alternativa preferisci gestire tali situazioni e in che modo strutturerai il codice qui sotto?

Ad esempio: -

// We pass both the wrapped class and the wrapping class as type parameters 
    // to the generic class allowing it to create instances of either as necessary. 

    public abstract class CoolClass<T, U> 
     where U : CoolClass<T, U>, new() 
    { 
     public T Value { get; private set; } 
     protected CoolClass() { } 
     public CoolClass(T value) { Value = value; } 
     public static implicit operator CoolClass<T, U>(T val) 
     { 
      // since we know the derived type and that its new(), we can 
      // new up an instance of it (which we couldn't do as an abstract class) 
      return new U() { Value = val}; 
     } 
     public static implicit operator T(CoolClass<T, U> obj) 
     { 
      return obj.Value; 
     } 
    } 

E una seconda domanda bonus: perché uno di questi operatori implicita di lavoro e l'altro non uno?

ad es.

public class CoolInt : CoolClass<int, CoolInt> 
    { 
     public CoolInt() { } 
     public CoolInt(int val) (val) { } 
    } 

            // Why does this not work 
     CoolInt x = 5; 
            // when this works 
     CoolInt x2 = (CoolInt)5;  
            // and this works 
     int j = x; 

risposta

1

È un po 'soggettivo ma non sono un grande fan dei cast impliciti. Il codice diventa spesso fuorviante quando vengono utilizzati e talvolta è difficile trovare un bug se è causato da cast di implisit. Se la tua classe è progettata solo per l'uso di essi, non la userei in questo modo.

perché UNO di questi operatori impliciti funziona e l'altro no?

Perché è stata definita la conversione da CoolClass<T, U>, ma non da CoolInt. Sono diversi tipi. Funzionerebbe se si ha questo metodo nell'implementazione CoolInt:

public static implicit operator CoolInt(int val) 

Circa l'utilizzo dei farmaci generici:

Tale utilizzo dei farmaci generici crea limitazioni alla propria architettura se si avrebbe bisogno di creare gerarchie di ereditarietà complesse con molte classi (ad esempio l'introduzione di un nuovo livello di astrazione potrebbe essere complicato). Ma questo dipende davvero da ciò di cui hai bisogno. In realtà ho usato tale tecnica in uno dei progetti per evitare la duplicazione del codice. Inoltre si poteva passare un delegato Func<U> al costruttore se il vostro CoolClass per superare nuove restrizioni() :)

+0

E la domanda sui parametri di tipo generico? (Avrei dovuto porre la domanda implicita in una domanda a parte perché è un po 'una distrazione per la domanda principale.) –

+0

Ho aggiornato la risposta. –

+0

+1 ma non sono d'accordo con la disapprovazione dei cast impliciti.Non esiste un codice che non possa essere scoperto senza l'analisi statica o l'uso esperto del debugger; e come sviluppatore mi aspetto di doverlo fare per analizzare codice più complesso. Il complesso non è male. In questo caso sta rendendo il consumatore della classe più capace di fare ciò che vuole. –

1

E 'un comune modello in C++, che sembra un po' come questo (e bene!):

template<typename T> class Base { 
    void foo() { T::foo(); /* Call the derived implementation*/ } 
}; 
class Derived : public Base<Derived> { 
    void foo() { /* do something*/ } 
}; 

Lo usiamo per il polimorfismo statico/ereditarietà, insieme ad altri.

Tuttavia, in .NET, dove i parametri generici sono in fase di esecuzione e vi è anche una riflessione, non sono del tutto sicuro di quale sarebbe il vantaggio. Voglio dire, avere il tipo derivato è utile, ma devi chiederti: utile per cosa, e in che cosa differisce dall'eredità diretta?

+0

+1 - ma quello che mi piacerebbe davvero essere in grado di fare in C# è il modello curiosamente ricorsivo - è un peccato che C# non abbia molto di un precompilatore di cui parlare! È interessante che tu abbia parlato di questo lato riflessivo delle cose: ho sviluppato un componente che ha compilato dinamicamente una classe derivante o l'implementazione di un'interfaccia per creare cose come il taglio trasversale. Anche se apprezzo la relativa facilità con cui questo tipo di cose possono essere fatte in. Net, questo ha detto che è una curva di apprendimento ripida. –

Problemi correlati