ประโยชน์ / ข้อเสียของการใช้switch
คำสั่งเทียบกับข้อif/else
ใน C # คืออะไร ฉันไม่สามารถจินตนาการได้ว่ามันมีความแตกต่างอย่างมากนอกเหนือไปจากโค้ดของคุณ
มีเหตุผลใดบ้างที่ทำให้ IL ที่ได้หรือประสิทธิภาพรันไทม์ที่เกี่ยวข้องนั้นแตกต่างกันอย่างสิ้นเชิง?
ประโยชน์ / ข้อเสียของการใช้switch
คำสั่งเทียบกับข้อif/else
ใน C # คืออะไร ฉันไม่สามารถจินตนาการได้ว่ามันมีความแตกต่างอย่างมากนอกเหนือไปจากโค้ดของคุณ
มีเหตุผลใดบ้างที่ทำให้ IL ที่ได้หรือประสิทธิภาพรันไทม์ที่เกี่ยวข้องนั้นแตกต่างกันอย่างสิ้นเชิง?
คำตอบ:
คำสั่ง SWITCH จะสร้างแอสเซมบลีเดียวกับ IF ในโหมดดีบักหรือความเข้ากันได้เท่านั้น ในการเปิดตัวมันจะถูกรวบรวมไว้ในตารางกระโดด (ผ่านคำสั่ง 'สวิตช์' ของ MSIL) - ซึ่งก็คือ O (1)
C # (ซึ่งแตกต่างจากภาษาอื่น ๆ ) ยังอนุญาตให้เปิดใช้ค่าคงที่สตริง - และสิ่งนี้ทำงานแตกต่างกันเล็กน้อย เห็นได้ชัดว่าไม่ได้มีประโยชน์ในการสร้างตารางกระโดดสำหรับสตริงที่มีความยาวตามอำเภอใจดังนั้นส่วนใหญ่สวิตช์ดังกล่าวจะถูกรวบรวมไว้ในสแต็กของ IF
แต่ถ้าจำนวนเงื่อนไขมีขนาดใหญ่พอที่จะครอบคลุมค่าใช้จ่ายคอมไพเลอร์ C # จะสร้างวัตถุ HashTable เติมด้วยค่าคงที่สตริงและทำการค้นหาบนตารางนั้นตามด้วยการกระโดด การค้นหา Hashtable นั้นไม่ใช่ O (1) และมีค่าใช้จ่ายคงที่ที่สังเกตได้ แต่ถ้าจำนวนของ case label มีขนาดใหญ่มันจะเร็วกว่าการเปรียบเทียบกับค่าคงที่ของสตริงใน IF
เพื่อสรุปให้ดีขึ้นถ้าจำนวนของเงื่อนไขมากกว่า 5 หรือมากกว่านั้นให้เลือก SWITCH มากกว่า IF หรือใช้สิ่งที่ดูดีกว่า
โดยทั่วไป (พิจารณาจากทุกภาษาและคอมไพเลอร์ทั้งหมด) คำสั่ง switch สามารถบางครั้งมีประสิทธิภาพมากกว่าคำสั่ง if / else เพราะมันง่ายสำหรับคอมไพเลอร์ในการสร้างตารางกระโดดจากคำสั่งสวิตช์ มันเป็นไปได้ที่จะทำสิ่งเดียวกันสำหรับคำสั่ง if / else ที่ได้รับข้อ จำกัด ที่เหมาะสม แต่นั่นยากกว่ามาก
ในกรณีของ C # สิ่งนี้ก็เป็นจริงเช่นกัน แต่ด้วยเหตุผลอื่น
ด้วยสตริงจำนวนมากจึงมีความได้เปรียบด้านประสิทธิภาพที่สำคัญในการใช้คำสั่ง switch เนื่องจากคอมไพเลอร์จะใช้ตารางแฮชเพื่อใช้การข้าม
ด้วยสตริงจำนวนน้อยประสิทธิภาพระหว่างสองจะเหมือนกัน
นี่เป็นเพราะในกรณีนั้นคอมไพเลอร์ C # ไม่ได้สร้างตารางการกระโดด แต่จะสร้าง MSIL ที่เทียบเท่ากับ IF / ELSE block
มีคำสั่ง "คำสั่ง switch" MSIL ซึ่งเมื่อ jitted จะใช้ตารางกระโดดเพื่อนำคำสั่ง switch ใช้ได้กับประเภทจำนวนเต็มเท่านั้น (คำถามนี้ถามเกี่ยวกับสตริง)
สำหรับสตริงจำนวนน้อยจะมีประสิทธิภาพมากขึ้นสำหรับคอมไพเลอร์ในการสร้างบล็อก IF / ELSE จากนั้นจะใช้ตารางแฮช
เมื่อตอนแรกที่ฉันสังเกตเห็นสิ่งนี้ฉันทำข้อสันนิษฐานว่าเนื่องจากบล็อก IF / ELSE ถูกใช้กับสตริงจำนวนเล็กน้อยคอมไพเลอร์ทำการแปลงแบบเดียวกันสำหรับสตริงจำนวนมาก
นี่มันผิด 'IMA' ใจดีมากพอที่จะชี้ให้ฉันเห็น (อืม ... เขาไม่ได้ใจดี แต่เขาพูดถูกและฉันผิดซึ่งเป็นส่วนสำคัญ)
ฉันยังได้ตั้งสมมติฐานเกี่ยวกับการขาดคำสั่ง "สวิตช์" ใน MSIL (ฉันคิดว่าถ้ามีสวิตช์ดั้งเดิมทำไมพวกเขาไม่ใช้กับตารางแฮชดังนั้นจึงต้องไม่มีสวิตช์ดั้งเดิม ... ) นี่เป็นทั้งความผิดและโง่อย่างไม่น่าเชื่อในส่วนของฉัน 'IMA' อีกครั้งชี้ให้ฉันเห็น
ฉันอัปเดตที่นี่เพราะเป็นโพสต์ที่ได้รับคะแนนสูงสุดและเป็นคำตอบที่ยอมรับได้
อย่างไรก็ตามฉันได้ทำให้ชุมชน Wiki เพราะฉันคิดว่าฉันไม่สมควรได้รับตัวแทนสำหรับการที่ผิด หากคุณมีโอกาสโปรดโพสต์โพสต์ของ 'ima'
สามเหตุผลที่ชอบswitch
:
ที่กำหนดเป้าหมายรหัสพื้นเมืองมักจะสามารถรวบรวมงบเปลี่ยนให้เป็นคอมไพเลอร์สาขาเงื่อนไขหนึ่งบวกกระโดดทางอ้อมในขณะที่ลำดับของif
s ต้องมีลำดับของสาขาเงื่อนไข ขึ้นอยู่กับความหนาแน่นของกรณีเอกสารจำนวนมากที่เรียนรู้ได้ถูกเขียนเกี่ยวกับวิธีรวบรวมคำสั่งกรณีอย่างมีประสิทธิภาพ บางส่วนมีการเชื่อมโยงจากหน้าคอมไพเลอร์ LCC (Lcc มีหนึ่งในคอมไพเลอร์ที่เป็นนวัตกรรมสำหรับสวิตช์)
คำสั่ง switch เป็นตัวเลือกระหว่างทางเลือกที่ไม่เหมือนกันและไวยากรณ์ของสวิตซ์ทำให้การควบคุมการไหลของข้อมูลมีความโปร่งใสมากขึ้นต่อโปรแกรมเมอร์จากนั้นรังของคำสั่ง if-then-else
ในบางภาษารวมถึง ML และ Haskell แน่นอนคอมไพเลอร์จะตรวจสอบเพื่อดูว่าคุณมีกรณีใดบ้างหรือไม่ ฉันมองว่าคุณสมบัตินี้เป็นหนึ่งในข้อได้เปรียบที่สำคัญของ ML และ Haskell ฉันไม่รู้ว่า C # สามารถทำได้หรือไม่
เรื่องเล็ก ๆ น้อย: ในการบรรยายที่เขาได้รับรางวัลสำหรับความสำเร็จตลอดชีวิตฉันได้ยิน Tony Hoare พูดว่าทุกสิ่งที่เขาทำในอาชีพของเขามีสามสิ่งที่เขาภาคภูมิใจที่สุด:
case
คำสั่ง)ฉันไม่สามารถจินตนาการได้โดยไม่ต้องอาศัยอยู่switch
คอมไพเลอร์จะปรับทุกอย่างให้เป็นโค้ดเดียวกันโดยมีความแตกต่างเล็กน้อย (Knuth, any?)
ความแตกต่างคือคำสั่งสวิทช์นั้นสะอาดกว่าสิบห้าถ้าหากมีข้อความอื่นมารวมกัน
เพื่อน ๆ จะไม่ปล่อยให้เพื่อนสแตกถ้า - อื่นงบ
จริง ๆ แล้ว switch statement นั้นมีประสิทธิภาพมากกว่า คอมไพเลอร์จะปรับให้เหมาะสมกับตารางค้นหาที่มีคำสั่ง if / else ไม่สามารถทำได้ ข้อเสียคือไม่สามารถใช้คำสั่ง switch กับค่าตัวแปรได้
คุณทำไม่ได้:
switch(variable)
{
case someVariable
break;
default:
break;
}
มันจะต้องเป็น
switch(variable)
{
case CONSTANT_VALUE;
break;
default:
break;
}
ฉันไม่เห็นคนอื่นยกระดับ (ชัดเจน?) ชี้ให้เห็นว่าข้อดีของประสิทธิภาพของคำสั่ง switch นั้นขึ้นอยู่กับหลาย ๆ กรณีที่มีแนวโน้มโดยประมาณเท่ากัน ในกรณีที่ค่าหนึ่ง (หรือสองสาม) มีโอกาสมากขึ้นบันได if-then-else อาจเร็วกว่ามากโดยตรวจสอบให้แน่ใจว่ามีการตรวจสอบกรณีที่พบบ่อยที่สุดก่อน:
ตัวอย่างเช่น:
if (x==0) then {
// do one thing
} else if (x==1) {
// do the other thing
} else if (x==2) {
// do the third thing
}
VS
switch(x) {
case 0:
// do one thing
break;
case 1:
// do the other thing
break;
case 2:
// do the third thing
break;
}
หาก x เท่ากับศูนย์ 90% ของเวลารหัส "if-else" อาจเร็วเป็นสองเท่าของรหัสที่ใช้สวิตช์ แม้ว่าคอมไพเลอร์เปลี่ยน "สวิทช์" ให้กลายเป็น goto ที่ขับเคลื่อนด้วยตารางบางชนิดมันก็ยังไม่เร็วเท่าการตรวจสอบศูนย์
switch
เข้ากันได้switch
คำสั่งจะดีกว่า (อ่านง่ายขึ้นบางครั้งเร็วขึ้น) หากคุณรู้ว่ากรณีหนึ่งมีโอกาสมากขึ้นคุณสามารถดึงมันออกมาเพื่อสร้างif
- else
- switch
และถ้ามันวัดได้เร็วกว่าคุณปล่อยให้มันเข้ามา (ทำซ้ำถ้าจำเป็น) IMO ที่ยังอ่านได้โดยมีเหตุผล หากความswitch
เสื่อมโทรมและเล็กเกินไป regex-replace จะทำงานส่วนใหญ่ในการแปลงให้เป็นelse if
-chain
บ่อยครั้งที่มันจะดูดีขึ้น - เช่นจะเข้าใจได้ง่ายขึ้นว่าเกิดอะไรขึ้น เมื่อพิจารณาถึงประโยชน์ที่ได้รับจากประสิทธิภาพการทำงานจะน้อยที่สุดอย่างมากมุมมองของรหัสคือความแตกต่างที่สำคัญที่สุด
ดังนั้นหาก if / else ดูดีขึ้นให้ใช้มันมิฉะนั้นให้ใช้คำสั่ง switch
หัวข้อด้านข้าง แต่ฉันมักจะกังวลเกี่ยวกับ (และบ่อยขึ้นเห็น) if
/ else
และswitch
คำสั่งได้รับวิธีที่มีขนาดใหญ่เกินไปกับกรณีมากเกินไป สิ่งเหล่านี้มักจะทำร้ายการบำรุงรักษา
ผู้ร้ายทั่วไป ได้แก่ :
เพื่อแก้ไข:
ตามลิงก์นี้IF vs Switchเปรียบเทียบการทดสอบการวนซ้ำโดยใช้สวิตช์และถ้ามีการใช้คำสั่งนั้นจะมีค่า 1,000,000,000 ซ้ำ, เวลาที่ใช้โดยคำชี้แจงสวิตช์ = 43.0s & โดยหากคำชี้แจง = 48.0s
ซึ่งแท้จริงแล้วคือ20833333การทำซ้ำต่อวินาทีดังนั้นหากเราต้องการที่จะมุ่งเน้นมากขึ้น
PS: เพียงเพื่อทราบความแตกต่างของประสิทธิภาพสำหรับรายการเงื่อนไขขนาดเล็ก
หากคุณเพียงแค่ใช้ if หรือคำสั่งอื่น ๆ โซลูชันพื้นฐานกำลังใช้การเปรียบเทียบ ผู้ประกอบการ
(value == value1) ? (type1)do this : (type1)or do this;
คุณสามารถทำหรือกิจวัตรประจำวันในสวิตช์
switch(typeCode)
{
case TypeCode:Int32:
case TypeCode.Int64:
//dosomething here
break;
default: return;
}
สิ่งนี้ไม่ได้ตอบคำถามของคุณ แต่เนื่องจากจะมีความแตกต่างเล็กน้อยระหว่างเวอร์ชันที่รวบรวมฉันจะขอให้คุณเขียนโค้ดของคุณในแบบที่อธิบายความตั้งใจของคุณได้ดีที่สุด ไม่เพียง แต่จะมีโอกาสที่ดีกว่าในการคอมไพเลอร์ทำสิ่งที่คุณคาดหวัง แต่มันจะทำให้มันง่ายขึ้นสำหรับคนอื่นที่จะรักษารหัสของคุณ
หากความตั้งใจของคุณคือการแยกโปรแกรมของคุณตามค่าของตัวแปร / คุณลักษณะหนึ่งคำสั่ง switch จะแสดงถึงความตั้งใจนั้นได้ดีที่สุด
หากความตั้งใจของคุณคือการแยกสาขาโปรแกรมของคุณตามตัวแปร / คุณสมบัติ / เงื่อนไขที่แตกต่างกันดังนั้นถ้า / อื่นถ้าเชนแสดงถึงความตั้งใจนั้นได้ดีที่สุด
ฉันจะให้โคดี้นั้นถูกต้องเกี่ยวกับคนที่ลืมคำสั่ง break แต่เกือบเท่าที่ฉันเห็นคนทำซับซ้อนหากบล็อกที่พวกเขาได้รับ {} ผิดดังนั้นบรรทัดที่ควรอยู่ในคำสั่งแบบมีเงื่อนไขไม่ได้ เป็นหนึ่งในเหตุผลที่ฉันมักจะรวม {} ไว้ในข้อความสั่งของฉันแม้ว่าจะมีหนึ่งบรรทัดในนั้น ไม่เพียง แต่จะง่ายต่อการอ่าน แต่ถ้าฉันต้องการเพิ่มอีกบรรทัดในเงื่อนไขฉันไม่สามารถลืมเพิ่มได้
คำถามที่น่าสนใจ สิ่งนี้เกิดขึ้นเมื่อไม่กี่สัปดาห์ที่ผ่านมาในที่ทำงานและเราพบคำตอบด้วยการเขียนตัวอย่างและดูใน. NET Reflector (reflector is Awesome !! i love it)
นี่คือสิ่งที่เราค้นพบ: คำสั่งเปลี่ยนที่ถูกต้องสำหรับสิ่งอื่นที่ไม่ใช่สตริงจะถูกคอมไพล์ไปยัง IL เพื่อเป็นคำสั่งเปลี่ยน อย่างไรก็ตามถ้ามันเป็นสตริงมันจะถูกเขียนใหม่เป็น if / else ถ้า / else ใน IL ดังนั้นในกรณีของเราเราต้องการทราบว่าการสลับคำสั่งเปรียบเทียบสตริงเช่นนั้นเป็นตัวพิมพ์เล็กและตัวสะท้อนแสงได้อย่างรวดเร็วทำให้เราได้คำตอบ สิ่งนี้มีประโยชน์ที่จะรู้
หากคุณต้องการเปรียบเทียบขนาดตัวพิมพ์เล็กและตัวใหญ่คุณสามารถใช้คำสั่ง switch ได้เร็วกว่าการใช้งาน String หากเปรียบเทียบกับ if / else (แก้ไข: อ่านอะไรที่เร็วกว่าเปิดสตริคหรือประเภทอื่นสำหรับการทดสอบประสิทธิภาพจริง ๆ ) อย่างไรก็ตามหากคุณต้องการใช้ตัวพิมพ์เล็กและตัวพิมพ์เล็กควรใช้ if / else เนื่องจากรหัสผลลัพธ์นั้นไม่สวย
switch (myString.ToLower())
{
// not a good solution
}
กฎที่ดีที่สุดของ thumb คือการใช้คำสั่ง switch หากเหมาะสม (จริงจัง) เช่น:
หากคุณต้องการปรับเปลี่ยนค่าให้ฟีดลงในคำสั่ง switch (สร้างตัวแปรชั่วคราวเพื่อสลับค่า) จากนั้นคุณอาจจะใช้คำสั่งควบคุม if / else
การปรับปรุง:
มันเป็นจริงดีกว่าที่จะแปลงสตริงเป็นตัวพิมพ์ใหญ่ (เช่นToUpper()
) ToLower()
ในฐานะที่ได้รับการเห็นได้ชัดว่ามีการเพิ่มประสิทธิภาพต่อไปว่าคอมไพเลอร์เพียงในเวลาที่สามารถทำตามที่เมื่อเปรียบเทียบกับ มันเป็นการเพิ่มประสิทธิภาพขนาดเล็ก แต่ในวงแคบ ๆ มันอาจมีประโยชน์
หมายเหตุเล็กน้อย:
เพื่อปรับปรุงความสามารถในการอ่านของ switch statement ลองปฏิบัติดังนี้:
คำสั่ง switch นั้นเร็วกว่าและถ้าเป็นอย่างอื่นแน่นอน มีการทดสอบความเร็วที่จัดทำโดย BlackWasp
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
- ตรวจสอบมัน
แต่ขึ้นอยู่กับความเป็นไปได้ที่คุณพยายามจะทำ แต่ฉันพยายามใช้คำสั่ง switch ทุกครั้งที่ทำได้
ไม่ใช่แค่ C # แต่ทุกภาษาที่ใช้ C ฉันคิดว่า: เนื่องจากสวิตช์ถูก จำกัด อยู่ที่ค่าคงที่จึงเป็นไปได้ที่จะสร้างรหัสที่มีประสิทธิภาพมากโดยใช้ "ตารางกระโดด" เคส C เป็นฟอร์แทรนเก่าที่คำนวณได้ดีของ GOTO แต่กรณี C # ยังคงทดสอบกับค่าคงที่
ไม่ใช่ในกรณีที่เครื่องมือเพิ่มประสิทธิภาพจะสามารถสร้างรหัสเดียวกันได้ พิจารณาเช่น
if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}
เพราะสิ่งเหล่านั้นเป็นสารประกอบบูลีนรหัสที่สร้างขึ้นจึงต้องคำนวณค่าและวงจรสั้น พิจารณาสิ่งที่เทียบเท่า
switch(a){
case 3: // ...
break;
case 5:
case 7: //...
break;
default: //...
}
สิ่งนี้สามารถรวบรวมได้
BTABL: *
B3: addr of 3 code
B5:
B7: addr of 5,7 code
load 0,1 ino reg X based on value
jump indirect through BTABL+x
เพราะคุณบอกคอมไพเลอร์ว่าไม่จำเป็นต้องคำนวณ OR และการทดสอบความเท่าเทียมกัน
อาจารย์ cs ของฉันไม่แนะนำให้คุณเปลี่ยนคำแถลงเพราะบ่อยครั้งที่คนลืมการพักหรือใช้อย่างไม่ถูกต้อง ฉันจำไม่ได้ว่าเขาพูดอะไร แต่มีบางอย่างตามเส้นที่มองที่ฐานรหัส seminal ที่แสดงตัวอย่างของคำสั่ง switch (ปีที่แล้ว) มีข้อผิดพลาดมากมายเช่นกัน
สิ่งที่ฉันเพิ่งสังเกตเห็นคือคุณสามารถรวม if / else และเปลี่ยน statement! มีประโยชน์มากเมื่อต้องตรวจสอบเงื่อนไขเบื้องต้น
if (string.IsNullOrEmpty(line))
{
//skip empty lines
}
else switch (line.Substring(0,1))
{
case "1":
Console.WriteLine(line);
break;
case "9":
Console.WriteLine(line);
break;
default:
break;
}
ฉันคิดว่าสวิตช์นั้นเร็วกว่าหากเงื่อนไขดูว่ามีโปรแกรมเช่น:
เขียนโปรแกรมเพื่อป้อนหมายเลขใด ๆ (ระหว่าง 1 - 99) และตรวจสอบว่ามันอยู่ในช่องไหน a) 1 - 9 จากนั้นสล็อตหนึ่ง b) 11 - 19 จากนั้นสล็อตสอง c) 21-29 จากนั้นสล็อตสามถึง 89- 99
ถ้าอย่างนั้นคุณจะต้องทำหลายเงื่อนไข แต่ลูกชายเปลี่ยนกรณีที่คุณต้องพิมพ์
สวิตช์ (ไม่ / 10)
และในกรณีที่ 0 = 1-9, กรณีที่ 1 = 11-19 และอื่น ๆ
มันจะง่ายมาก
มีอีกหลายตัวอย่างเช่น!
คำสั่ง switch โดยพื้นฐานคือการเปรียบเทียบความเท่าเทียมกัน เหตุการณ์แป้นพิมพ์มีข้อได้เปรียบเหนือคำสั่งสวิตช์เมื่อมีการเขียนและอ่านรหัสได้ง่ายและถ้ามีคำสั่ง if หากไม่มี {Bracket} ก็อาจทำให้เกิดปัญหาได้เช่นกัน
char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}
คำสั่ง ifif ถ้ายอดเยี่ยมสำหรับโซลูชันมากกว่าหนึ่งวิธีถ้า (theAmountOfApples มากกว่า 5 && theAmountOfApples น้อยกว่า 10) บันทึกแอปเปิ้ลของคุณเป็นอย่างอื่นถ้า (theAmountOfApples == 100) ขายแอปเปิ้ลของคุณ ฉันไม่ได้เขียน c # หรือ c ++ แต่ฉันเรียนรู้มาก่อนที่ฉันจะเรียนรู้ java และพวกเขาเป็นภาษาที่ใกล้เคียง
ข้อเสียที่เป็นไปได้ข้อหนึ่งของคำสั่งสวิตช์คือการขาดเงื่อนไขหลายข้อ คุณสามารถมีหลายเงื่อนไขสำหรับ if (อื่น ๆ ) แต่ไม่ได้มีหลายกรณีที่มีเงื่อนไขต่างกันในสวิตช์
คำสั่ง Switch ไม่เหมาะสำหรับการดำเนินการทางตรรกะเกินขอบเขตของสมการ / นิพจน์บูลีนแบบง่าย สำหรับสมการบูลีน / นิพจน์นั้นจะเหมาะสมอย่างเห็นได้ชัด แต่ไม่ใช่สำหรับการดำเนินการเชิงตรรกะอื่น ๆ
คุณมีอิสระมากขึ้นด้วยตรรกะที่มีอยู่ในคำสั่ง If แต่ความสามารถในการอ่านอาจประสบได้หากคำสั่ง If กลายเป็นเรื่องไม่สะดวกหรือมีการจัดการที่ไม่ดี
ทั้งสองมีสถานที่ขึ้นอยู่กับบริบทของสิ่งที่คุณเผชิญ