2009-04-01 5 views
65

Nei modelli C++, è possibile specificare che un determinato parametro di tipo è un valore predefinito. Cioè se non specificato esplicitamente, utilizzerà il tipo T.Esiste un approccio ragionevole ai parametri di tipo "predefinito" in C# Generics?

Questo può essere fatto o approssimato in C#?

Sto cercando qualcosa di simile:

public class MyTemplate<T1, T2=string> {} 

In modo che un'istanza del tipo che non specifica esplicitamente T2:

MyTemplate<int> t = new MyTemplate<int>(); 

Sarebbe essenzialmente:

MyTemplate<int, string> t = new MyTemplate<int, string>(); 

In definitiva sto osservando un caso in cui esiste un modello che è abbastanza ampio sed, ma sto valutando l'espansione con un parametro di tipo aggiuntivo. Potrei sottoclassi, immagino, ma ero curioso di sapere se c'erano altre opzioni in questo senso.

risposta

61

La sottoclasse è l'opzione migliore.

avrei sottoclasse vostra principale classe generica:

class BaseGeneric<T,U>

con una specifica classe

class MyGeneric<T> : BaseGeneric<T, string>

Ciò rende più semplice per mantenere la logica in un unico luogo (la classe base) , ma anche facile da fornire entrambe le opzioni di utilizzo. A seconda della classe, c'è probabilmente molto poco lavoro extra necessario per fare in modo che ciò accada.

+1

ah ... questo ha un senso.Il nome del tipo può essere lo stesso se i parametri del tipo forniscono una firma univoca? – el2iot2

+2

@ee: sì, i generici sono "overloadabili" dal conteggio dei parametri. –

+0

@ee: Sì, ma sarei diffidente nel farlo. È "legale" in .NET farlo, ma può creare confusione. Preferirei che il nome del tipo derivato dalla stringa fosse simile alla classe generica principale (quindi è ovvio quale sia/facile da trovare), ma un nome che renda ovvio che si tratta di una stringa. –

6

C# non supporta tale funzione.

Come hai detto, puoi creare una sottoclasse (se non è sigillata e duplicare tutte le dichiarazioni del costruttore) ma è una cosa completamente diversa.

1

Sfortunatamente C# non supporta ciò che si sta tentando di fare. Sarebbe una caratteristica difficile da implementare dato che il tipo predefinito per un parametro dovrebbe rispettare i vincoli generici e molto probabilmente creerebbe mal di testa quando il CLR cercava di garantire la sicurezza del tipo.

+1

Non proprio. Avrebbe potuto essere fatto con un attributo (come i parametri di default in VB.NET) e fare in modo che il compilatore lo sostituisca al momento della compilazione. La ragione principale sono gli obiettivi di design C#. –

+0

Il compilatore deve assicurarsi che il parametro predefinito soddisfi i vincoli generici. Anche il parametro predefinito sarebbe un vincolo generico stesso poiché qualsiasi ipotesi fatta sul parametro type nel metodo richiederebbe che qualsiasi parametro di tipo non predefinito ereditasse da esso. –

+0

@Andrew, il parametro predefinito non deve essere un vincolo generico. Se questo si dovesse comportare più come i parametri predefiniti del template in C++, estendendosi alla classe degli automatoni sarebbe perfettamente corretto fare: MyTemplate x = null Perché T2 non ha vincoli generici, quindi float va bene nonostante il tipo predefinito di stringa . In questo modo i parametri predefiniti del template sono essenzialmente solo "zucchero sintattico" per scrivere MyTemplate come abbreviazione di MyTemplate . –

13

Una soluzione è una sottoclasse. Un altro che vorrei usare, invece, sono i metodi factory (combinati con la parola chiave var).

public class MyTemplate<T1,T2> 
{ 
    public MyTemplate(..args..) { ... } // constructor 
} 

public static class MyTemplate{ 

    public static MyTemplate<T1,T2> Create<T1,T2>(..args..) 
    { 
     return new MyTemplate<T1, T2>(... params ...); 
    } 

    public static MyTemplate<T1, string> Create<T1>(...args...) 
    { 
     return new MyTemplate<T1, string>(... params ...); 
    } 
} 

var val1 = MyTemplate.Create<int,decimal>(); 
var val2 = MyTemplate.Create<int>(); 

Nell'esempio sopra val2 è di tipo MyTemplate<int,string>e non un tipo derivato da esso.

Un tipo class MyStringTemplate<T>:MyTemplate<T,string> non è dello stesso tipo di MyTemplate<T,string>. Ciò potrebbe causare alcuni problemi in alcuni scenari. Ad esempio, non è possibile trasmettere un'istanza di MyTemplate<T,string> a MyStringTemplate<T>.

+3

Questo è l'approccio più utilizzabile. Soluzione molto bella –

8

è anche possibile creare un sovraccarico di classe in questo modo

public class MyTemplate<T1, T2> { 
    public T1 Prop1 { get; set; } 
    public T2 Prop2 { get; set; } 
} 

public class MyTemplate<T1> : MyTemplate<T1, string>{} 
+0

Leggere altre risposte prima di inviare una risposta tardiva, perché probabilmente la soluzione è la stessa di altre. –

+4

la risposta accettata è stata la creazione di una classe con un nome diverso, la mia soluzione è il sovraccarico della stessa classe – Moes

+1

No, entrambi stanno creando una nuova classe. Il nome non ha importanza qui. 'MyTemplate ' è una classe diversa da 'MyTemplate ', né 'AnotherTemplate '. –