2014-11-14 11 views
8

Il compilatore ottimizzerà questo codice o la raccolta verrà inizializzata dopo ogni chiamata di metodo?Il compilatore ottimizzerà l'inizializzazione delle raccolte?

private string Parse(string s) 
{ 
    var dict = new Dictionary<string, string> 
    { 
     {"a", "x"}, 
     {"b", "y"} 
    }; 

    return dict[s]; 
} 

Se la risposta è negativa, vi consiglio di utilizzare questa soluzione: Creating a constant Dictionary in C#

+0

Bene, il GC lo esporrà in modo prolifico dopo l'ambito del metodo, sebbene non sia deterministico. – DevEstacion

+0

Puoi spiegare perché pensi che un compilatore possa ottimizzare questo e come? – CodeCaster

+1

@CodeCaster Perché: il compilatore può dipendere da classi .NET specifiche e questa situazione può essere rilevata facilmente. Come: renderlo statico, ad esempio – astef

risposta

13

Quando sorge una domanda del genere e non sei sicuro di quale sia la risposta, è sempre bene guardare "sotto il cofano".

Questa è la IL il compilatore generato con l'ottimizzatore acceso:

Parse: 
IL_0000: newobj  System.Collections.Generic.Dictionary<System.String,System.String>..ctor 
IL_0005: stloc.1  // <>g__initLocal0 
IL_0006: ldloc.1  // <>g__initLocal0 
IL_0007: ldstr  "a" 
IL_000C: ldstr  "x" 
IL_0011: callvirt System.Collections.Generic.Dictionary<System.String,System.String>.Add 
IL_0016: ldloc.1  // <>g__initLocal0 
IL_0017: ldstr  "b" 
IL_001C: ldstr  "y" 
IL_0021: callvirt System.Collections.Generic.Dictionary<System.String,System.String>.Add 
IL_0026: ldloc.1  // <>g__initLocal0 
IL_0027: stloc.0  // dict 
IL_0028: ldloc.0  // dict 
IL_0029: ldarg.1  
IL_002A: callvirt System.Collections.Generic.Dictionary<System.String,System.String>.get_Item 
IL_002F: ret  

Come si può vedere, si chiama newobj di assegnare le Dictionary<K,V>ogni volta, carichi sia tra i residenti e chiede Dictionary.Add ogni volta su entrambi (che è l'equivalente zucchero sintattico di chiamare Add). Non ha alcuna conoscenza intima con il tipo per memorizzare nella cache la creazione degli oggetti.

+2

Il risultato di questa ispezione potrebbe cambiare in qualsiasi momento. È molto meglio ragionare che il compilatore non è autorizzato a farlo mai. – usr

+0

@usr Hai ragione, ecco perché ho dichiarato che il compilatore non ha alcuna conoscenza intima con il tipo. Anche se dire * "l'ispezione potrebbe cambiare in qualsiasi momento" * è una dichiarazione piuttosto generale, che può essere vera per una qualsiasi delle risposte seguenti. –

2

No, C# così com'è non sarà 'ottimizzare' questo in alcun modo - e ho seriamente dubbio che sarà più come prima Astuccio.

Mentre si potrebbe sostenere che questo particolare codice potrebbe essere ottimizzato, si tratta in realtà di un caso limite, che non è così banale da determinare in fase di compilazione. Per darti un esempio di contatore: cosa succede se uno dei tuoi valori letterali stringa è un membro di un'altra classe? Cosa succede se si dispone di un dizionario personalizzato (e non esiste una gestione speciale per la classe dizionario esistente) che ha fatto qualcosa di funky nel suo costruttore?

+0

"e se uno dei tuoi valori letterali stringa era un membro di un'altra classe?" - Non riesci a vedere niente di male, puoi spiegare? Hai ragione sui dizionari personalizzati, ma riguardo al solito dizionario. È già accoppiato con un compilatore (vedi initializer) – astef

+0

Immagina di avere 'new Dictinary {{" x ", SOME_CLASS.MEMBER}}'. Ora quando 'SOME_CLASS.MEMBER' cambia, una chiamata conseguente a 'Parse' dovrebbe usare il nuovo valore - quindi non può essere ottimizzata. – decPL

+0

Penso che tu abbia frainteso i tipi di riferimento in C#. L'ottimizzazione possibile sta solo rendendo il dizionario un campo statico: verrà inizializzato solo una volta. Che problema vedi in questo caso? – astef

6

No, non lo farà. Non ha alcuna conoscenza intrinseca di cosa sia un Dictionary. Per il compilatore, è solo una classe normale, quindi non lo sa. potrebbe riutilizzare l'istanza in questo caso particolare.

questo sarebbe il modo corretto di fare questo:

public class Something 
{ 
    private static readonly Dictionary<string, string> _dict = new Dictionary<string, string> 
    { 
     {"a", "x"}, 
     {"b", "y"} 
    } 

    private string Parse(string s) 
    { 
     return _dict[s]; 
    } 
} 

Questo approccio funziona perché si sa che cosa l'oggetto fa e si sa anche che non è mai modificato.

Ricordate la seguente sintassi:

var dict = new Dictionary<string, string> 
{ 
    {"a", "x"}, 
    {"b", "y"} 
} 

è solo zucchero sintattico per questo:

var dict = new Dictionary<string, string>(); 
dict.Add("a", "x"); 
dict.Add("b", "y"); 

L'unico requisito per fare questo lavoro con qualsiasi classe, è per la classe a

  • Implementare IEnumerable
  • Avere un metodo pubblico Add.

È suggerito di utilizzare un'istruzione switch, ma l'approccio Dictionary può essere più flessibile. Si consideri ad esempio che si potrebbe voler utilizzare un comparatore di uguaglianza diverso (come StringComparer.OrdinalIgnoreCase). È meglio lasciare che sia lo Dictionary a gestirlo utilizzando il confronto corretto piuttosto che usare qualcosa come switch(value.ToLowerInvariant()).

+0

Ci sono molti esempi in cui il compilatore dipende da specifiche classi .NET. Perché non "dizionario"? – astef

+0

@astef perché dovrebbe? Cosa c'è di così speciale in un 'dizionario'? Perché non un 'HashSet' allora? o 'OrderedDictionary', o' ConditionalWeakTable'? Questo sarebbe senza fine. È meglio avere solo alcuni casi speciali noti (mi viene in mente 'Task ) –

+0

Cosa c'è di così speciale nel metodo' Add' per sostituirlo con l'inizializzatore? Onestamente, non lo so. Basta chiedere) – astef

Problemi correlati