เปลี่ยนคำสั่งผ่าน ... ควรได้รับอนุญาตหรือไม่? [ปิด]


120

ตราบเท่าที่ฉันจำได้ฉันได้หลีกเลี่ยงการใช้คำสั่ง switch fall-through จริงๆแล้วฉันจำไม่ได้เลยว่ามันเข้าสู่จิตสำนึกของฉันเป็นวิธีที่เป็นไปได้ในการทำสิ่งต่างๆในขณะที่มันถูกเจาะเข้ามาในหัวของฉันในช่วงต้นว่ามันไม่มีอะไรมากไปกว่าข้อผิดพลาดในคำสั่งสวิตช์ อย่างไรก็ตามวันนี้ฉันพบโค้ดบางอย่างที่ใช้โดยการออกแบบซึ่งทำให้ฉันสงสัยทันทีว่าทุกคนในชุมชนคิดอย่างไรเกี่ยวกับคำสั่งสวิตช์ที่ผิดพลาด

เป็นสิ่งที่ภาษาโปรแกรมไม่ควรอนุญาตอย่างชัดเจน (เช่น C # แม้ว่าจะมีวิธีแก้ปัญหา) หรือเป็นคุณลักษณะของภาษาใด ๆ ที่มีประสิทธิภาพเพียงพอที่จะปล่อยให้อยู่ในมือของโปรแกรมเมอร์หรือไม่?

แก้ไข: ฉันไม่ได้เจาะจงเพียงพอกับสิ่งที่ฉันหมายถึงโดยการตกผ่าน ฉันใช้ประเภทนี้บ่อยมาก:

    switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

อย่างไรก็ตามฉันกังวลเกี่ยวกับเรื่องนี้

   switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something, but fall through to the other cases
            // after doing it.

        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

ด้วยวิธีนี้เมื่อใดก็ตามที่เคสเป็น 0 1 มันจะทำทุกอย่างในคำสั่ง switch ฉันเคยเห็นสิ่งนี้จากการออกแบบและฉันไม่รู้ว่าฉันเห็นด้วยหรือไม่ว่าควรใช้คำสั่ง switch ด้วยวิธีนี้ ฉันคิดว่าตัวอย่างโค้ดแรกเป็นประโยชน์และปลอดภัยมาก อย่างที่สองดูเหมือนจะอันตราย



51
ไม่เห็นด้วยเกี่ยวกับการไม่สร้างสรรค์ 27 คะแนนโหวตเกี่ยวกับคำถามกล่าวว่ามีคนอื่น ๆ ที่รู้สึกแบบนี้เช่นกัน
Wes Modes

2
"ไม่สร้างสรรค์" เป็นเหตุผลใกล้ตัวที่เก่าแก่และค่อนข้างคลุมเครือ ปัจจุบันคำถามดังกล่าวสามารถและควรได้รับการตั้งค่าสถานะปิดว่าเป็นไปตามความคิดเห็นเท่านั้น SO มีไว้สำหรับคำถามที่ตอบได้เกี่ยวกับโค้ดหรือการออกแบบไม่ใช่ "ความคิดเห็นของ" ชุมชน "[อะไรก็ได้] เกี่ยวกับความคิดเห็นของฉันเกี่ยวกับฟีเจอร์ X" โปรแกรมเมอร์ SE จะเหมาะสมกว่า แต่ถึงอย่างนั้นคุณค่าของคำถามที่กว้างและขับเคลื่อนด้วยความคิดเห็นยังคงเป็นที่น่าสงสัยอย่างมาก
underscore_d

1
มีการใช้งานที่ชัดเจนมากที่ใช้การผิดพลาดดังนั้นจึงไม่แน่ใจว่าเหตุใดจึงเป็นไปตามความเห็น ฉันยอมรับว่าคำถามนี้ค่อนข้างไม่ชัดเจน แต่ก็ไม่ควรปิดเพราะไม่สร้างสรรค์ อาจจะเพิ่งแก้ไขให้เป็นกรณีที่ชัดเจนยิ่งขึ้นของ: "เมื่อใดที่ใช้ fallthrough" ซึ่งมีคำตอบชัดเจน. ข้อความ C ++ ที่ดีมีมากกว่านี้มันทำให้ผู้เขียนโค้ดใหม่สับสน (หรือผู้เขียนโค้ดที่มีประสบการณ์มากกว่าจากภาษาอื่น ๆ ) ดูเหมือนจะเป็นคำถามที่ดีสำหรับ SO อันที่จริงดูเหมือนว่าเป็นคำถาม SO ต้นแบบที่ดี แม้ว่าวิธีที่ถามมันจะไม่สมบูรณ์แบบก็ตาม
eric

คำตอบ:


89

อาจขึ้นอยู่กับสิ่งที่คุณคิดว่าผิดพลาด ฉันโอเคกับสิ่งนี้:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

แต่ถ้าคุณมีป้ายกำกับเคสตามด้วยรหัสที่ตกไปยังป้ายกำกับเคสอื่นฉันก็มักจะพิจารณาถึงความชั่วร้ายนั้น บางทีการย้ายรหัสทั่วไปไปยังฟังก์ชันและการโทรจากทั้งสองแห่งจะเป็นความคิดที่ดีกว่า

และโปรดทราบว่าฉันใช้นิยามคำถามที่พบบ่อย C ++ของ"ความชั่วร้าย"


ฉันเห็นด้วยกับคุณ: ฉันไม่คิดว่าตัวอย่างของคุณเป็นข้อผิดพลาดและหากมีกรณีที่ฉันต้องการเรียกใช้โค้ดหลายบล็อกผ่านทางลัดอาจมีวิธีที่ดีกว่าในการทำเช่นนั้น
Dave DuPlantis

10
+1 ง่ายๆสำหรับคำจำกัดความของ "ความชั่วร้าย" ฉันจะยืมมันขอบคุณ ;-)
Joachim Sauer

ฉันเข้าใจประเด็นของคุณและคุณพูดถูก แต่ตัวอย่างของคุณแย่มาก ไม่มีใครควรทดสอบตัวเลขทดสอบเช่นนี้ Scnr คำตอบของคุณถูกต้องอยู่แล้ว
Tim Büthe

ในกรณีนี้นั่นคือสิ่งที่ C # ไม่อนุญาต อาจมีเหตุผล :-)
โจอี้

น่าเศร้าที่ความหมายของความชั่วร้ายที่ดูเหมือนว่าจะได้ไปแบบออฟไลน์
HopefullyHelpful

57

มันเป็นดาบสองคม บางครั้งก็มีประโยชน์มาก แต่มักเป็นอันตราย

เมื่อไหร่ดี? เมื่อคุณต้องการ 10 กรณีทั้งหมดดำเนินการในลักษณะเดียวกัน ...

switch (c) {
  case 1:
  case 2:
            ... Do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... Do something ...
            break;
  case 5:
  case 43:
            ... Do something else ...
            break;
}

กฎข้อหนึ่งที่ฉันชอบคือถ้าคุณเคยทำอะไรแปลก ๆ โดยที่คุณไม่รวมช่วงพักคุณต้องมีความคิดเห็นที่ชัดเจน / * FALLTHROUGH * / เพื่อระบุว่านั่นคือความตั้งใจของคุณ


3
+1! ฉันยอมรับว่าบางครั้งคำตอบที่ถูกต้องและฉันสองเท่ายอมรับว่าเมื่อเป็นไปตามการออกแบบควรแสดงความคิดเห็น (ในการตรวจสอบโค้ดฉันจะทำให้นักพัฒนารายอื่นแสดงความคิดเห็นโดยการออกแบบที่ไม่ว่างเปล่าไม่ใช่ว่ามันเกิดขึ้นเราเป็นร้าน C # ที่ไม่รองรับ :)
John Rudy

4
ฉันเองใช้ / * FALL THROUGH * / แสดงความคิดเห็นในรหัสของฉันตามความเหมาะสม =]
strager

2
หากคุณ PC-Lint คุณต้องใส่ / * - fallthrough * / มิฉะนั้นจะบ่น
Steve Melnikoff

1
ถึงกระนั้นหากคุณต้องการทำให้ชัดเจนว่าพฤติกรรมนั้นเป็นไปโดยเจตนาคุณก็สามารถif(condition > 1 && condition <10) {condition = 1}; switch(...ช่วยคุณได้เช่นกัน (เพื่อให้ดูชัดเจน) และทุกคนจะเห็นว่าคุณต้องการให้กรณีเหล่านั้นกระตุ้นการกระทำแบบเดียวกัน
ทำเครื่องหมาย

1
นี่ยังคงเป็นการเข้ารหัสที่แย่มาก กรณีแรกควรเรียกใช้ฟังก์ชันกรณีที่สองควรเรียกใช้ฟังก์ชันเดียวกันจากนั้นเรียกฟังก์ชันที่สอง
BlueRaja - Danny Pflughoeft

21

คุณเคยได้ยินเกี่ยวกับอุปกรณ์ของ Duffหรือไม่? นี่เป็นตัวอย่างที่ยอดเยี่ยมของการใช้สวิตช์ล้มเหลว

เป็นคุณลักษณะที่สามารถใช้ได้และสามารถใช้ในทางที่ผิดได้เช่นเดียวกับคุณลักษณะภาษาเกือบทั้งหมด


เรียนรู้สิ่งใหม่ ๆ ทุกวัน - ขอบคุณ! ฉันรู้สึกถึงนักพัฒนาที่น่าสงสารที่ไปแม้ว่าจะมีการเปลี่ยนแปลงเช่นนี้
Justin R.

ว้าวนั่นเป็นสิ่งที่ห่อหุ้มสมองของคุณ
Fostah

6
หมายเหตุความคิดเห็นที่ยกมาของ Duff ในบทความนั้น: "รหัสนี้ก่อให้เกิดข้อโต้แย้งบางอย่างในการอภิปรายนั้น แต่ฉันไม่แน่ใจว่าเป็นข้อโต้แย้งหรือไม่"
John Carter

อุปกรณ์ของ Duff นั้นชั่วร้าย
Marjeta

21

การล้มลงเป็นสิ่งที่มีประโยชน์จริงๆขึ้นอยู่กับว่าคุณกำลังทำอะไรอยู่ พิจารณาวิธีจัดเรียงตัวเลือกที่เป็นระเบียบและเข้าใจง่ายนี้:

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

ลองนึกภาพการทำเช่นนี้กับ if / else มันจะวุ่นวาย


1
ฉันพบว่าการล้มเหลวประเภทนี้มีประโยชน์มาก
Mitchel Sellers

ถ้า 'ทำอะไรบางอย่าง' มากกว่าสวิตช์จะยุ่งกว่า if / else เมื่อใช้ if / else จะเห็นได้ชัดว่าตัวแปรใดถูกทดสอบ แม้แต่ตัวอย่างเล็ก ๆ นี้ก็ไม่ได้ดูแย่ขนาดนี้กับ if / else ในสายตาของฉัน
grenix

10

อาจมีประโยชน์มากไม่กี่ครั้ง แต่โดยทั่วไปแล้วพฤติกรรมที่ต้องการจะไม่ตก ควรอนุญาตให้ตกได้ แต่ไม่ใช่โดยปริยาย

ตัวอย่างในการอัปเดตเวอร์ชันเก่าของข้อมูลบางส่วน:

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}

6

ฉันชอบไวยากรณ์ที่แตกต่างกันสำหรับทางเลือกในสวิตช์บางอย่างเช่นผิดพลาด ..

switch(myParam)
{
  case 0 or 1 or 2:
    // Do something;
    break;
  case 3 or 4:
    // Do something else;
    break;
}

หมายเหตุ: สิ่งนี้จะเกิดขึ้นได้ด้วย enums ถ้าคุณประกาศทุกกรณีใน enum โดยใช้แฟล็กใช่ไหม? มันฟังดูไม่เลวเหมือนกัน กรณีนี้สามารถ (ควร?) เป็นส่วนหนึ่งของ enum ของคุณได้เป็นอย่างดี

บางทีนี่อาจเป็นกรณีที่ดี (ไม่มีการเล่นสำนวน) สำหรับอินเทอร์เฟซที่คล่องแคล่วโดยใช้วิธีการขยาย? บางสิ่งบางอย่างผิดพลาด ...

int value = 10;
value.Switch()
  .Case(() => { /* Do something; */ }, new {0, 1, 2})
  .Case(() => { /* Do something else */ } new {3, 4})
  .Default(() => { /* Do the default case; */ });

แม้ว่าอาจจะอ่านได้น้อยลงก็ตาม: P


1
ฉันชอบคำแนะนำแรกมาก ซึ่งอ่านได้ดีมากและช่วยประหยัดโค้ดได้ไม่กี่บรรทัด อย่างที่สองใช้เวลาสักครู่ในการห่อสมองของฉัน แต่ก็มีเหตุผล แต่มันก็ดูเป็นระเบียบ
Fostah

1
ใช่นั่นคือสิ่งที่ฉันคิดเช่นกันมันเป็นเพียงการผายลมแบบสุ่มที่ฉันใส่ลงไปในความพยายามที่จะหาวิธีอื่นในการเปลี่ยนโดยใช้โครงสร้างภาษาที่มีอยู่
Erik van Brakel

ผ่านคำถามนี้และฉันก็ชอบสิ่งนี้จริงๆ! มีความคิดเกี่ยวกับวิธีปรับปรุงความสามารถในการอ่านได้ แต่ SO ไม่อนุญาตให้ฉันโพสต์ความคิดเห็นแบบหลายบรรทัด :( บางคนควรใช้สิ่งนี้เช่นเดียวกับ POC ดูสนุกในการนำไปใช้และใช้งาน (:
Victor Elias

5

เช่นเดียวกับสิ่งอื่น ๆ : หากใช้ด้วยความระมัดระวังอาจเป็นเครื่องมือที่สวยงาม

อย่างไรก็ตามฉันคิดว่ามีข้อเสียมากกว่าที่จะไม่ใช้มันและสุดท้ายก็ไม่อนุญาตอีกต่อไป (C #) ในบรรดาปัญหา ได้แก่ :

  • การ "ลืม" การหยุดพักเป็นเรื่องง่าย
  • ไม่ชัดเจนเสมอไปสำหรับผู้ดูแลรหัสว่าการหยุดพักที่ละเว้นนั้นเป็นเจตนา

การใช้สวิตช์ / เคสกันตกอย่างดี:

switch (x)
{
case 1:
case 2:
case 3:
 Do something
 break;
}

Baaaaad การใช้สวิตช์ / กรณีตกผ่าน:

switch (x)
{
case 1:
    Some code
case 2:
    Some more code
case 3:
    Even more code
    break;
}

สิ่งนี้สามารถเขียนใหม่ได้โดยใช้ if / else สร้างโดยไม่มีการสูญเสียเลยในความคิดของฉัน

คำพูดสุดท้ายของฉัน: อยู่ห่างจากป้ายกำกับตัวพิมพ์เล็ก ๆ น้อย ๆ เช่นเดียวกับในตัวอย่างที่ไม่ดีเว้นแต่คุณจะรักษารหัสเดิมที่ใช้สไตล์นี้และเข้าใจดี


3
คุณสามารถเขียนโครงสร้างภาษาส่วนใหญ่ซ้ำได้โดยใช้ if () และ goto ... ซึ่งไม่ได้แสดงให้เห็นว่าการละทิ้งโครงสร้างที่ชัดเจน
Shog9

2
ฉันรู้ แต่โครงสร้างสวิตช์ / เคสที่ใช้ "case 1: some code case 2: some more code case 3: final code break;" สามารถและควรเขียนใหม่โดยใช้ if / else if (ความคิดเห็นของฉัน)
steffenj

1
โปรดทราบว่าสวิตช์อาจเร็วกว่ารายการ if-elses หลายเท่า สวิตช์ใช้ตารางการกระโดดหรือเมื่อทำไม่ได้การกระโดดตามเงื่อนไขจะถูกจัดโครงสร้างในโครงสร้างการตัดสินใจแทนที่จะเป็นรายการเชิงเส้นของการกระโดดตามเงื่อนไข คำสั่ง If-else แบบซ้อนที่มีโครงสร้างดีจะเร็วที่สุดเท่ากัน
Jochem Kuijpers

4

มันมีพลังและอันตราย ปัญหาที่ใหญ่ที่สุดของการล่มสลายคือมันไม่ชัดเจน ตัวอย่างเช่นหากคุณพบโค้ดที่มีการแก้ไขบ่อยซึ่งมีสวิตช์ที่มีการล้มเหลวคุณจะรู้ได้อย่างไรว่านั่นเป็นความตั้งใจและไม่ใช่จุดบกพร่อง

ทุกที่ที่ฉันใช้ฉันมั่นใจว่ามีการแสดงความคิดเห็นอย่างถูกต้อง:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }

2
เจตนาจะชัดเจนหากไม่มีรหัสสำหรับกรณี "แรก" หากคุณเขียนโค้ดไว้ที่นั่นและต้องการเรียกใช้โค้ดสำหรับ case 'second' แสดงว่าความคิดเห็นนั้นสำคัญ แต่ฉันจะหลีกเลี่ยงสถานการณ์นั้นอยู่ดี
Joel Coehoorn

1
@ Joel - ฉันเห็นด้วยอย่างสมบูรณ์ ในร้านของฉันเรามีคนตกอยู่ในความคิดเห็นในกรณีเช่นนี้และฉันคิดว่ามันช่วยลดความสามารถในการอ่าน หากมีรหัสอยู่ให้ใส่ความคิดเห็นที่ขาดหายไป
Fred Larson

เราหมายถึงอะไร "ไม่ชัดแจ้ง" นี่คือเหตุผลที่เราทำลาย; ภาษาอื่นก็ทำเช่นนี้ เป็นเพียงปัญหาสำหรับผู้ที่ไม่ได้เรียนรู้ภาษาของตนตามลำดับ C # ต้องการมันสำหรับ ff กรณีเริ่มต้นโดยไม่มีเหตุผลที่จะ imho
mckenzm

3

การใช้ Fall-through เหมือนในตัวอย่างแรกของคุณนั้นถือว่าใช้ได้อย่างชัดเจนและฉันจะไม่ถือว่ามันเป็นการล้มเหลวอย่างแท้จริง

ตัวอย่างที่สองเป็นอันตรายและ (หากไม่ได้แสดงความคิดเห็นอย่างกว้างขวาง) ไม่ชัดเจน ฉันสอนนักเรียนว่าอย่าใช้โครงสร้างดังกล่าวเว้นแต่พวกเขาจะคิดว่ามันคุ้มค่ากับความพยายามที่จะทุ่มเทบล็อกความคิดเห็นซึ่งอธิบายว่านี่เป็นการล้มเหลวโดยเจตนาและเหตุใดวิธีการแก้ปัญหานี้จึงดีกว่าทางเลือกอื่น สิ่งนี้ไม่สนับสนุนการใช้งานที่เลอะเทอะ แต่ก็ยังอนุญาตให้ใช้ในกรณีที่ใช้เพื่อประโยชน์

สิ่งนี้เทียบเท่ากับสิ่งที่เราทำในโครงการอวกาศไม่มากก็น้อยเมื่อมีคนต้องการละเมิดมาตรฐานการเข้ารหัสพวกเขาต้องยื่นขอการจ่ายยา (และฉันถูกเรียกให้ไปให้คำแนะนำเกี่ยวกับการพิจารณาคดี)


2

ฉันไม่ชอบswitchให้คำพูดของฉันตกไป - มันมีโอกาสผิดพลาดและอ่านยากเกินไป ยกเว้นอย่างเดียวคือเมื่อหลายcaseงบทุกคนทำว่าสิ่งเดียวกัน

หากมีรหัสทั่วไปที่หลายสาขาของคำสั่งสวิตช์ต้องการใช้ฉันแยกข้อมูลนั้นออกเป็นฟังก์ชันทั่วไปแยกต่างหากที่สามารถเรียกใช้ในสาขาใดก็ได้


1

ในบางกรณีการใช้ fall-through เป็นการกระทำของความเกียจคร้านในส่วนของโปรแกรมเมอร์ - พวกเขาสามารถใช้ชุดของ || ตัวอย่างเช่น แต่ใช้ชุดของกรณีสวิตช์ 'catch-all' แทน

ดังที่กล่าวมาฉันพบว่าพวกเขามีประโยชน์อย่างยิ่งเมื่อฉันรู้ว่าในที่สุดฉันก็ต้องการตัวเลือกอยู่ดี (เช่นในการตอบกลับเมนู) แต่ยังไม่ได้ใช้ตัวเลือกทั้งหมด ในทำนองเดียวกันหากคุณกำลังทำ Fall-through ทั้ง 'a' และ 'A' ฉันพบว่าการใช้สวิตช์ fall-through นั้นสะอาดกว่าการใช้คำสั่ง if

อาจเป็นเรื่องของรูปแบบและวิธีการคิดของโปรแกรมเมอร์ แต่โดยทั่วไปแล้วฉันไม่ชอบที่จะลบองค์ประกอบของภาษาในชื่อ 'ความปลอดภัย' - ซึ่งเป็นเหตุผลว่าทำไมฉันถึงชอบภาษา C และตัวแปร / ลูกหลานมากกว่าพูดว่า ชวา ฉันชอบที่จะลิงไปรอบ ๆ ด้วยตัวชี้และสิ่งที่ชอบแม้ว่าฉันจะไม่มี "เหตุผล" ก็ตาม


พูดอย่างเคร่งครัด คำสั่งสวิตช์จะข้ามผ่านค่าที่กำหนดไปยังตำแหน่งรหัสเฉพาะจำนวนมาก ในขณะที่คอมไพเลอร์น่าจะจับสิ่งเหล่านี้ได้ แต่ก็มีค่าความเร็วและความถูกต้องสำหรับคำสั่ง switch เหนือชุดหรือคำสั่ง ถ้า p เป็น 0 หรือ p คือ 1 หรือ p คือ 2 ฉันต้องประเมินว่า p เป็น 0 และ p เป็น 1 แทนที่จะข้ามไปที่ตำแหน่ง p = 2 ในโค้ด ซึ่งบังเอิญเหมือนกันกับ p0 และ p1 แต่ฉันไม่เคยตรวจสอบเลย
Tatarize

1

ควรใช้ Fall-through เฉพาะเมื่อใช้เป็นตารางกระโดดลงในบล็อกโค้ด หากมีส่วนใดส่วนหนึ่งของโค้ดที่มีการแบ่งโดยไม่มีเงื่อนไขก่อนที่จะมีกรณีมากกว่านี้กลุ่มเคสทั้งหมดควรจบลงด้วยวิธีนั้น

สิ่งอื่นใดคือ "ความชั่วร้าย"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.