2015-04-23 14 views
6

Diciamo che voglio avere una classe generica Box che può contenere qualcosa all'interno, quindi è una Box<T>. Box<T> ha un metodo che restituisce un TransformBox<U>:Modelli di template curiosamente ricorrenti con tipi generici aggiuntivi

public Box<U> Transform<U>(Func<T, U> transform) 

Finora questo è stato abbastanza semplice. Tuttavia, in realtà ho bisogno di un abstract Box, poiché il modo in cui i valori sono racchiusi e trasformati è specifico dell'implementazione. (Non posso avere un'interfaccia poiché ci sono altri metodi che sono implementati tramite la composizione di metodi astratti, ma questo non cambia comunque).

Naturalmente, voglio che i miei metodi sostituiti Transform restituiscano una sottoclasse appropriata di Box, non Box stesso. Dal momento che tipi restituiti di metodi imperativi sono invarianti in C#, mi rivolgo al curiously recurring template pattern (vedi IComparable<T>):

public abstract class Box<B, T> where B : Box<B, T> 

Ora ogni classe I ereditare da Box<B, T> dovrebbe riferirsi a se stesso o si scatena l'inferno:

public class FooBox<T> : Box<FooBox, T> 

Tuttavia, questo distrugge completamente il metodo Transform:

public abstract Box<B, U> Transform<U>(Func<T, U> transform); 

fallisce la compilazione con The type 'B' cannot be used as type parameter 'B' in the generic type or method 'Test.Box<B,T>'. There is no implicit reference conversion from 'B' to 'Test.Box<B,U>'. (CS0311). Questo ha senso, dal momento che il tipo di ritorno è ora Box<B, U> e B è Box<B, T>, che non è Box<B, U>.

La correzione semplice non funziona:

public abstract Box<B, U> Transform<U>(Func<T, U> transform) where B : Box<B, U>; 

non riesce a compilare con 'Test.Box<B,T>.Transform<U>()' does not define type parameter 'B' (CS0699).

C'è un modo per risolvere questo, o mi sono davvero dipinto in un angolo?

+0

si può solo fare un '' Box e trasmettere informazioni specifiche implementazione attraverso metodi di fabbrica? –

+0

@MillieSmith rimuovendo il CRTP funzionerebbe, naturalmente, ma ciò risulterebbe nel non poter controllare quei metodi come 'Box Unisci (Riquadro un altro, Func unione)' effettivamente accetta 'Box'es compatibili. – Alexey

risposta

1

Penso che il problema con la correzione diretta sia il riutilizzo del parametro di tipo B. Provare qualcosa di diverso, e includerlo come un parametro di tipo:

public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) where B2 : Box<B2, U>; 

Update: lei ha dichiarato:

ora non posso garantire che B2 e B sono in realtà la stessa classe derivata, che era il mio obiettivo

Questo non è il caso, però, non B2 (non può?) ereditare da B, U potrebbe ereditare T se lo si desidera. Puoi includerlo come vincolo. Ciò non è strettamente necessario per il modello, dal momento che è fino al corpo di Transform per risolverlo.

es:

public abstract Box<B2, U> Transform<B2,U>(Func<T, U> transform) 
    where B2 : Box<B2, U> 
    where U : T 
+0

Non al 100% che sia necessario 'B2' anziché' B', ma ne hai sicuramente bisogno come parametro di tipo per 'Transform' –

+0

che compila (se aggiungo B2 ai parametri generici del metodo), ma ora non posso garantire che B2 e B sono in realtà la stessa classe derivata, che era il mio obiettivo. – Alexey

+0

risposta aggiornata. –

Problemi correlati