compilare il seguente codice.
public static int Main(string[] args)
{
switch (args[0])
{
case "x": return 1;
case "y": return 2;
case "z": return 3;
}
return 0;
}
Ora usano Reflector o ILDASM per esaminare l'IL il compilatore C# genera. Continuate ad aggiungere dichiarazioni di caso e decompilare e osservare il risultato.
- Se il numero di istruzioni caso è piccolo, il compilatore emette un confronto di uguaglianza sequenziale.
- Se il numero di istruzioni caso è elevato, il compilatore emette una ricerca
Dictionary
.
Stavo usando il compilatore C# 3.0 e ho osservato che la strategia cambia in 7 case statement. Sospetto che vedrai qualcosa di simile a C# 4.0 e altri.
Aggiornamento:
Tengo a precisare che si vedrà chiamate al Dictionary.Add
nell'output IL, dove sta costruendo il dizionario per un uso successivo. Non fatevi ingannare nel pensare che questo accada ogni volta. Il compilatore sta effettivamente generando una classe statica separata e sta eseguendo un'inizializzazione statica incorporata. Prestare particolare attenzione alle istruzioni su L_0026. Se la classe è già inizializzata, il ramo salterà sulle chiamate Add
.
L_0021: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1
L_0026: brtrue.s L_0089
L_0028: ldc.i4.7
L_0029: newobj instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::.ctor(int32)
L_002e: dup
L_002f: ldstr "x"
L_0034: ldc.i4.0
L_0035: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
L_003a: dup
L_003b: ldstr "y"
L_0040: ldc.i4.1
L_0041: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
L_0046: dup
L_0047: ldstr "z"
L_004c: ldc.i4.2
L_004d: call instance void [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::Add(!0, !1)
Inoltre, si noti che il dizionario contiene effettivamente una mappa dalla stringa originale a un numero intero. Questo intero viene utilizzato per formulare un interruttore separato in IL.
L_0089: volatile.
L_008b: ldsfld class [mscorlib]System.Collections.Generic.Dictionary`2<string, int32> <PrivateImplementationDetails>{816396DD-F271-4C12-83D0-CC9C9CD67AD6}::$$method0x6000001-1
L_0090: ldloc.2
L_0091: ldloca.s CS$0$0002
L_0093: call instance bool [mscorlib]System.Collections.Generic.Dictionary`2<string, int32>::TryGetValue(!0, !1&)
L_0098: brfalse.s L_00da
L_009a: ldloc.3
L_009b: switch (L_00be, L_00c2, L_00c6, L_00ca, L_00ce, L_00d2, L_00d6)
L_00bc: br.s L_00da
L_00be: ldc.i4.1
L_00bf: stloc.1
L_00c0: br.s L_00de
L_00c2: ldc.i4.2
L_00c3: stloc.1
L_00c4: br.s L_00de
L_00c6: ldc.i4.3
Aggiornamento 2:
Per quello che vale la pena di VB.NET non sembra avere la stessa ottimizzazione per la sua Select
costrutto.
Hai visto la mia risposta? Ho specificamente menzionato che le istruzioni switch sulla stringa sono ottimizzate in una ricerca 'Dictionary' se il numero di istruzioni caso raggiunge una certa soglia. –
Brian, hai appena visto quella parte del tuo post. Puoi indicarmi la documentazione su questo? Sto trovando risposte contrastanti. Grazie per l'aiuto. –
Avrai difficoltà a trovare documentazione ufficiale (al di fuori del blog post occasionale) riguardante l'argomento. Il motivo è perché questo è un dettaglio di implementazione. –