2015-07-24 5 views
6

Sto cercando un modo per ereditare i generici con parametrizzazioni ereditate - o se ciò non è possibile, il modo più vicino per ottenere la stessa funzionalità.Ereditare i generici con parametrizzazioni ereditate

Si consideri il seguente:

classe B eredita dalla classe A

Classe D eredita dalla classe C

Ora, ho una classe:

abstract class A<T> where T : C 

con il costruttore:

public A(T t) 

Ora, desidero estendere la classe A come così:

class B<D> : A<C> 

Creazione di un costruttore:

public B(D t) : base(t){ /*stuff here*/} 

Tuttavia, questo genera un errore in fase di compilazione, dal momento che non è D C. Così la mia due domande sono:

a) C'è un modo pulito per fare questo? Nel peggiore dei casi, penso di poter sostituire D con C con un piccolo problema, ma forse c'è una ragione per cui non è un'idea sicura?

b) Devo anche dichiarare esplicitamente i miei generici nella definizione di classe figlio o esiste un modo più semplice che dovrei fare con tipi vincolati?

+2

Non si vede lo scenario in cui lo si utilizza, ma 'A dove T1: T2 dove T2: C' e' B : A 'dovrebbe fare il trucco. – MarcinJuraszek

risposta

2

Aggiungere un cui vincolo alla classe B

class B<D> : A<C> where D : C 

di rispondervi domande:

a) Con la correzione in questa risposta, il tuo approccio è abbastanza pulito finora, se un po 'troppo astratto (dovuto principalmente ai nomi).

b) Si dovrebbe aggiungere solo il parametro di tipo generico D alla classe B del bambino se la classe figlio stessa sta aggiungendo valore in modo generico relativo alle sottoclassi di C che sono istanze di D; o se la classe bambino è essa stessa incompleta, si prevede che sarà estesa e richiede sottoclassi di conoscenza di C come D. In tal caso, dovresti contrassegnarlo come astratto.

UPDATE:

ho voluto aggiungere una cosa su questo. Nella firma sopra, il parametro T di A<T> sarà C in quei membri di A che utilizzano il tipo T. Questo può essere un problema, come illustrato nel seguente esempio:

public class C {} 

public class F : C {} 

public class E : C {} 

public class A<T> where T : C 
{ 
    protected T cSubclass; 
    public void SetCSubclass(T cSubclass) { this.cSubclass = cSubclass; } 
} 

public class B<D> : A<C> where D : C 
{ 
    public D GetCSubclass() 
    { 
     return this.cSubclass; 
    } 
} 

Il codice in questo esempio non viene compilato.Si otterrà il seguente errore di compilazione:

error CS0266: Cannot implicitly convert type 'C' to 'D'. An explicit conversion exists (are you missing a cast?) 

Tuttavia l'errore di compilazione viene risolto se cambiamo di classe B al seguente:

public class B<D> : A<D> where D : C 
{ 
    public D GetCSubclass() 
    { 
     return this.cSubclass; 
    } 
} 

La ragione è che D è quello di essere una sottoclasse specifica di C tuttavia nella versione precedente abbiamo limitato A solo a qualsiasi forma di C, inclusi se stesso e le sue sottoclassi. Pertanto potremmo potenzialmente chiamare new B<F>.SetCSubclass(new E());, che sarebbe un tipo diverso da quello che GetCSubclass si aspetta di restituire. Nell'ultima versione abbiamo specificato D come argomento di tipo da utilizzare in A forzando B<F>.SetCSubclass per accettare solo istanze di F.

Ciò fornisce un ulteriore grado di sicurezza del tipo che uno sviluppatore che utilizza questo tipo di modello potrebbe aspettarsi.

+0

@ user650261 Volevo aggiungere ancora una cosa a riguardo. Sei sicuro di non volere "classe B : A dove D: C {}". La differenza con questa firma alternativa è che ovunque A ritorna o accetta T in sottoclassi di B, T sarà D. Nella firma fornita nella mia risposta, T sarà C in quei membri di A che potrebbero non essere ciò che alla fine vuoi, poiché se T è usato per un parametro di metodo, potresti finire per ricevere una sottoclasse di C che non è una sottoclasse di D. –