2016-03-12 20 views
7

Se si implementa un tipo generico che rischia di essere istanziato con un sacco di parametri di tipo, dovrei evitare (per motivi JIT di prestazioni/codice, ecc.) Avere molti tipi non generici nidificati?Devo evitare i tipi annidati nei tipi generici?

Esempio:

public class MyGenericType<TKey, TValue> 
{ 
    private struct IndexThing 
    { 
     int row; int col; 
    } 

    private struct SomeOtherHelper 
    { 
     .. 
    } 

    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

L'alternativa che funziona ugualmente bene è avere i tipi non generici fuori, ma poi inquinano il namespace. Esiste una best practice?

public class MyGenericType<TKey, TValue> 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 
} 

internal struct IndexThingForMyGenericType 
{ 
    int row; int col; 
} 

internal struct SomeOtherHelper 
{ 
    ... 
} 
+0

Come sempre non ottimizzare mai senza mai colmare i colli di bottiglia. Basta codificare in un modo che ha più senso, soprattutto non sarà un problema.Per quanto riguarda la domanda, non lo so, ma puoi vedere un sacco di classi private nidificate in BCL, quindi non mi dispiacerebbe andare su quella strada, dal punto di vista del design. Il secondo, come dici tu, può confondere lo – nawfal

+0

Questo codice non è semanticamente uguale. Nel tuo primo esempio, 'SomeOtherHelper' non è un singolo tipo in quanto dipende dai parametri generici. Ad esempio, 'typeof (MyGenericType .SomeOtherHelper)' non è uguale a 'typeof (MyGenericType .SomeOtherHelper)'. – Enigmativity

+0

Se il tuo problema è avere le classi nello spazio dei nomi perché vuoi che sia più pulito, quindi aggiungilo a un sottospazio, ad esempio se il tuo spazio dei nomi è MyNamespace poi aggiungi poi a MyNamespace.AccesoryNamespace. – Gusman

risposta

6

In C# ogni tipo annidato di un tipo generico è intrinsecamente generico. Il compilatore renderà il tipo annidato anche generico (a nostra insaputa). Per ulteriori informazioni, consultare this article.

Sebbene i generici condividano il codice JIT per i tipi di riferimento come spiegato in this interview, presenta un sovraccarico rispetto alle classi non generiche. Ogni tipo di valore ottiene il proprio codice JIT.

  • Se il tipo viene utilizzato solo nella classe generica --È ha più senso di essere un tipo annidato privato.

  • Se il tipo è utilizzato altrove, dovrebbe essere idealmente un tipo non annidato (come interno).

Detto questo, se i tipi nidificati non sta usando il tipo di parametro T in questo caso, non deve essere un tipo nidificato di tipo generico e quindi diventare un tipo generico pure.

maggior parte delle volte non dovrebbe importa, ma se siete preoccupati di molti tipi creati in fase di esecuzione, è possibile refactoring del tipo generico di avere una classe base non generica che agisce come un tipo di contenitore per i tipi nidificati e esporre i tipi nidificati come protetti.

public class NonGenericBase 
{ 
    protected struct IndexThing 
    { 
     int row; int col; 
    } 

    protected struct SomeOtherHelper 
    { 
     .. 
    } 
} 

public class MyGenericType<TKey, TValue> : NonGenericBase 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

In questo modo si condividono gli stessi tipi annidati. Nessun sovraccarico di runtime. Nessun tipo separato per ogni parametro di tipo. Ora typeof(MyGenericType<int, string>.SomeOtherHelper) sarà uguale a typeof(MyGenericType<long, bool>.SomeOtherHelper).

+0

Penso che la classe base non generica potrebbe essere la strada da percorrere. L'unica cosa negativa è che se non è effettivamente utile, deve ancora essere (almeno così) pubblico come il mio tipo generico, quindi ora ho inquinato la API pubblica e non solo quella interna :( Continuo a pensare che questa sia la la soluzione meno cattiva comunque. Grazie –

1

Anche se questo non risponde pienamente alla domanda, di notare che per i tipi di riferimento il codice è condiviso, dato che internamente è tutta una questione puntatori. Quindi non devi preoccuparti di gonfiare il codice base. Citazione da this risposta (Anders Hejlsberg è il quota).

Ora, ciò che poi facciamo è per tutte le istanze di tipo che sono il valore tipi, ad esempio List<int>, List<long>, List<double>, List<float> - creiamo una copia unica del codice nativo eseguibile. Quindi List<int> ottiene il proprio codice. List<long> ottiene il proprio codice. List<float> ottiene il proprio codice . Per tutti i tipi di riferimento condividiamo il codice, perché sono identici a . Sono solo dei puntatori.

+0

Sì, ma non è nemmeno condiviso per tipi di valore della stessa dimensione, quindi 'MyGenericType .IndexThing' afaik. non riutilizzerà 'MyGenericType .IndexThing' ecc. Con 2+ parametri di tipo temo un'esplosione combinatoria anche se tutte le istanze di riferimento condividono il codice! –

+0

@AndersForsgren Sì, lo so. Ecco perché ho detto che è condiviso per i tipi * reference *. – Kapol