Switch的C#內部實現
https://www.cnblogs.com/Interkey/p/3730432.html
在IL匯編語言中的Switch指令 -- 按照標號來進行跳轉(和goto語句中的標號相同)
執行IL中Switch指令時,從運算棧頂彈出一個無符號整數,然后跳轉到整數對應的標號位置繼續執行
如果整數值沒有對應的標號,則忽略switch指令,調到switch指令之后的一條指令開始執行。
詳細分析:
-- 結論 (實驗過程見原網頁)
1. 整數參數的Switch語句
1a. 連續的整數
c#的switch的case語句對應IL的switch指令中的case子句
1b. 不連續的但是相近的整數
c#的switch的case語句對應IL的switch指令中的case子句,
但是對於case指令之間的"縫隙"整數,會自動跳轉default子句的地址
1c. 很不連續的整數
如果按1b的思路,縫隙很大的話,IL中switch指令會憑空增加很多指向default子句指向地址的case子句
編譯器不使用switch指令,而是使用了beq指令 -- 取值若相等則跳轉到目標位置,否則繼續下一個取值
2. 枚舉類型的Switch語句
與對待整數沒有差別,因為枚舉值就是按照整數對待的;如果枚舉成員的取值不連續,則對應1b或1c
3. string類型的Switch語句
3a. case子句數量<=4時
string是引用類型參數
同樣在IL中沒有使用switch指令
如果參數為null的話,則執行流程直接跳轉到case null的指令塊中
否則,比較參數與case語句對應string的相等性(==),若相等,則跳轉到對應的地址后,跳出switch
-- 這就相當於被編譯成了一連串的if語句
那么,當case子句過多時,豈不是會導致程序變慢?
3b. case子句數量>4時
如果不是null的話,則會實例化一個字典泛型類 System.Collections.Generic.Dictionary`2<string,int32>
將分別出現在case子句中的string作為key插入到字典中,每個key的value分別對應從0開始的整數(switch子句的序號)
調用字典的TryGetValue()嘗試從字典中找到string參數所對應的字典元素
如果在字典中沒找到,則跳轉到default子句對應的位置
如果找到了,這里出現了switch指令,根據從字典中取到的value整數值,進行switch子句的跳轉
switch之后的一條指令則為一個無條件跳轉,直接跳轉到default子句
這里是當switch指令在棧頂取到的整數值比switch指令中跳轉地址數量要大時,忽略switch直接執行之后的指令
總結:
3b情況下,由於Dictionary<TKey,TValue>類型通過key來取值的時間復雜度接近於O(1) -- 有助於提高效率
為了微乎其微的效率提升
1. 盡量在switch中使用連續的取值
2. 如果取值不連續,則使用盡量少的case子句,並將會出現頻率高的case放在前面(與if...else if...else類似)
3. 如果使用了大量if語句來判斷一個字符串對象是否具有某值,改用Switch
4. 有其他引用類型對象想要使用switch判斷但又不能使用時,可以按照3b的思路自己實現。