2012-03-20 14 views
5

Perché non è possibile aprire i tipi generici come parametri. Ho spesso classi come:C# Perché non è possibile aprire i tipi generici come parametri?

public class Example<T> where T: BaseClass 
{ 
    public int a {get; set;} 
    public List<T> mylist {get; set;} 
} 

Diciamo che BaseClass è come segue;

public BaseClass 
{ 
    public int num; 
} 

Allora voglio un metodo di dire:

public int MyArbitarySumMethod(Example example)//This won't compile Example not closed 
{ 
    int sum = 0; 
    foreach(BaseClass i in example.myList)//myList being infered as an IEnumerable 
     sum += i.num; 
    sum = sum * example.a; 
    return sum; 
} 

Poi ho dovuto scrivere un'interfaccia solo per passare questo una classe come parametro come segue:

public interface IExample 
{ 
public int a {get; set;} 
public IEnumerable<BaseClass> myIEnum {get;} 
} 

il generico la classe deve essere modificata in:

public class Example<T>: IExample where T: BaseClass 
{ 
    public int a {get; set;} 
    public List<T> mylist {get; set;} 
    public IEnumerable<BaseClass> myIEnum {get {return myList;} } 
} 

Questo è un sacco di cerimonie per quello che avrei pensato che il compilatore potesse dedurre. Anche se qualcosa non può essere cambiato, trovo psicologicamente molto utile se conosco le ragioni/giustificazioni per l'assenza di scorciatoie di sintassi.

+3

Non è molto chiaro di quale restrizione stai parlando, poiché non hai mostrato come stai cercando di passarlo. Sospetto * Ho un'idea di cosa intendi, ma la domanda non è chiara ... –

+0

Cosa intendi quando chiedi "[come aprire] un tipo generico"? –

+0

@Rich, come passarebbe sintatticamente 'Esempio ' (anche in linea di principio) se 'T' non è stato specificato? –

risposta

2

Sono stato in una situazione simile, ma non ho mai avuto questa (molto buona!) Domanda. Ora che sono costretto a pensare a questo proposito, ecco la mia risposta:

Si prevede che il seguente al lavoro:

void F(Example<> e) { 
    Console.WriteLine(e.a); //could work 
} 

Sì, questo potrebbe teoricamente lavorare, ma questo non lo farà:

void F(Example<> e) { 
    Console.WriteLine(e.mylist); //?? type unknown 
} 

Si aspetta che il compilatore e il CLR siano in grado di analizzare i corpi del metodo e dimostrare che nel primo caso non è possibile un accesso davvero non sicuro. Che potrebbe essere fatto funzionare. Perché non è stato? Probabilmente, il design non è davvero sano e confuso. Inoltre, "tutte le funzionalità non sono implementate per impostazione predefinita. Qualcuno deve implementarle, testarle e documentarle".

Modifica: desidero sottolineare che questa funzione non può essere implementata senza la collaborazione del CLR. Il compilatore C# deve emettere il metodo esatto da chiamare che non è possibile su un tipo generico aperto. Il sistema di tipi non ammette nemmeno una tale variabile in questo momento.

+1

In effetti tutte le funzionalità non sono implementate di default, ma io uso molto i generics e mi trovo ripetutamente a digitare sempre lo stesso schema. Sono sorpreso che altri non abbiano sentito un forte bisogno di questa funzionalità. La chiusura di un tipo è essenzialmente una forma di ereditarietà. –

+0

La mia esperienza mi dice che questo succede a volte, ma di rado. Sicuramente, tutto dipende dal progetto. Ma il mio punto principale è che questa funzione è probabilmente confusa ("perché posso accedere a una lista ma non a mylist?" "Perché funziona ma non è così?") E che probabilmente non è facile trovare una buona soluzione che funzioni in assolutamente tutti i casi. – usr

+0

Forse non scrivo codice C# tipico. Devo confessare nelle ore buie della notte, i pensieri che Smalltalk e Lisp hanno attraversato la mia mente. –

2

Un razionale di implementazione: supportare ciò richiederebbe che tutti i metodi di classe generici non privati ​​siano resi virtuali, altrimenti non sarebbe possibile conoscere quale metodo specifico richiedere un tipo generico "aperto". Esistono diversi jittings dei diversi metodi di tipo chiuso, con diversi indicatori di metodo corrispondenti. Definire un'interfaccia manualmente corrisponde a istruire il compilatore quali metodi devono essere trattati come virtuali e consente inoltre di specificare esattamente quale sottoinsieme della funzionalità "aperta" che si desidera esporre. Quello che stai proponendo è fondamentalmente che tutte le classi generiche hanno una classe di interfaccia generata implicitamente per la porzione "aperta" della loro interfaccia pubblica, che ha implicazioni di prestazioni per la classe anche se questa funzione non viene mai utilizzata.

+0

In realtà, la situazione è peggiore. Se un 'Foo ' ha per es. un campo statico 'Bar' di tipo' int', 'Foo .Bar',' Foo .Bar', 'Foo .Bar',' Foo .Bar', ecc. si riferiscono tutti a diverse posizioni di memorizzazione che potrebbe avere valori diversi A quale di questi si riferisce "Foo <>. Bar"? Dato che non esiste un modo generale in cui un compilatore che chiama un membro su alcuni 'Foo ' sarebbe in grado di dire se quel membro possa accedere direttamente o indirettamente a 'Foo .Bar', non avrebbe modo di sapere se qualche tentativo di chiamare 'Foo <>' senza un parametro potrebbe essere sicuro. – supercat

1

Forse non sono comprendere la vostra domanda esatta, ma si può fare il vostro "MyArbitrarySumMethod" fare un esempio arbitrario dichiarando in questo modo:

public int MyArbitarySumMethod<T>(Example<T> example) where T : BaseClass 

È quindi possibile chiamare senza specificare "T":

int sum = MyArbitrarySumMethod(myExampleInstance); 

È quello che stai cercando?

+0

@ KirkWoll: No, non la penso così. Questo approccio è esattamente quello che stavo per suggerire. –

+0

@ Jon, suppongo che tu abbia ragione. Mi era sembrato che l'OP stesse cercando un modo generale per passare un tipo generico in giro senza avere un riferimento a 'T' - dove la soluzione generale * è * scrivere un wrapper di interfaccia per esso. Ma per il caso d'uso specifico di un metodo, hai ragione che funzionerà. –

Problemi correlati