การใช้งานที่เหมาะสมของคำสั่งเปลี่ยนผ่าน


14

เมื่อใดที่เหมาะสมที่จะใช้คำสั่ง switch-fall (classic) มีการแนะนำและสนับสนุนการใช้งานดังกล่าวหรือไม่และควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมดหรือไม่?


ไม่ใช่ทุกภาษาที่อนุญาตให้ใช้คำสั่งสลับบน
Oded

@Oded, แก้ไข, เพิ่มคำว่า "classic" ไม่ได้ทุกภาษาอนุญาตให้ตกผ่าน แต่ผมยืนยันว่ามันเป็นคลาสสิก)
shabunc

3
หากคุณกำลังพูดถึงอุปกรณ์ Duffsนั่นเป็นสิ่งหนึ่ง
Oded

6
@Oded: นั่นเป็นสิ่งแรกที่คนส่วนใหญ่คิด แต่ก็ไม่ค่อย "เหมาะสม" อ้างอิงจากเรื่องนี้ดัฟฟ์เองพูดว่า"นี่เป็นข้อโต้แย้งในการอภิปรายว่าควรเปลี่ยนผ่านโดยค่าเริ่มต้น แต่ฉันไม่แน่ใจว่าเรื่องนี้มีไว้สำหรับหรือต่อต้านมัน"
BlueRaja - Danny Pflughoeft

คำตอบ:


15

นี่คือตัวอย่างที่มันจะมีประโยชน์

public Collection<LogItems> GetAllLogItems(Level level) {
    Collection<LogItems> result = new Collection<LogItems>();
    switch (level) {
        // Note: fall through here is INTENTIONAL
        case All:
        case Info:
             result.Add(GetItemsForLevel(Info));
        case Warning:
             result.Add(GetItemsForLevel(Warning));
        case Error:
             result.Add(GetItemsForLevel(Error));
        case Critical:
             result.Add(GetItemsForLevel(Critical));
        case None:
    }
    return result;
}

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


13
ตัวอย่างนี้ในขณะที่น่าสนใจไม่มี// INTENTIONAL FALL THROUGH HEREความคิดเห็นที่จำเป็นสำหรับตัวพิมพ์ใหญ่ทั้งหมด
Russell Borogove

มันง่ายเหมือนการเขียนรหัสนี้โดยการล้มโดยการGetItemsForLevel(Info)เรียกใช้GetItemsForLevel(Warning)และอื่น ๆ
Caleb

@RussellBorogove: ถูกต้องอย่างแน่นอน
กำหนดค่า

@Caleb: ในกรณีนี้ใช่ แต่ถ้าสายนั้นซับซ้อนกว่านี้อีกล่ะ? มันอาจมีขนเล็กน้อยในขณะที่การผ่านทำให้ดูเหมือนง่าย ฉันควรทราบว่าจริง ๆ แล้วฉันไม่ชอบการใช้การตกหล่น - ฉันแค่บอกว่ามันมีประโยชน์ในบางสถานการณ์
กำหนดค่า

ไม่ใช่ตัวอย่างที่ดีที่สุด มีการจัดลำดับของระดับคำเตือน ด้วยการกำหนดการสั่งซื้อวิธีการนี้จะลดรายการการกรองรหัสบรรทัดเดียวเมื่อระดับรายการอย่างน้อยเท่ากับระดับที่ต้องการ
วินไคลน์

8

ฉันใช้พวกเขาเมื่อต้องใช้การทำงานบางอย่างกับค่ามากกว่าหนึ่งค่า ตัวอย่างเช่นสมมติว่าคุณมีวัตถุที่มีคุณสมบัติชื่อ operationCode ถ้ารหัสเท่ากับ 1, 2, 3 หรือ 4 คุณต้องการ startOperationX () ถ้าเป็น 5 หรือ 6 คุณต้องการ startOperationY () และ 7 ที่คุณ startOperationZ () เหตุใดจึงมี 7 กรณีที่สมบูรณ์พร้อมฟังก์ชั่นและตัวแบ่งเมื่อคุณสามารถใช้ fall-throughs ได้?

ฉันคิดว่ามันใช้ได้อย่างสมบูรณ์ในบางสถานการณ์โดยเฉพาะอย่างยิ่งถ้ามันหลีกเลี่ยงคำสั่ง if-else 100 ข้อ =)


4
สิ่งที่คุณกำลังอธิบายคือการที่switchมีหลายcases เชื่อมโยงกับรหัสเดียวกัน นั่นแตกต่างจากการล้มลงโดยที่การประมวลผลอันใดอันหนึ่งcaseดำเนินต่อไปสู่อีกอันหนึ่งเนื่องจากขาดbreakระหว่างพวกเขา
Blrfl

3
มันไม่สำคัญเลยการตกลงมาเป็นเพียงการขาดคำสั่งหยุดพักดังนั้นมันจึง "ผ่าน" กับกรณีถัดไป
Yatrix

ฉันจะอธิบายสถานการณ์ในคำตอบของคุณอีกครั้งเพื่อสะท้อนถึงสิ่งนั้น
Blrfl

2
@Yatrix: ฉันไม่เห็นด้วย มันแตกต่างกันมากที่จะมีกรณีที่ต่อเนื่องกันทั้งหมดที่ดำเนินการบล็อกเดียวกันของรหัสที่แน่นอนกว่าที่จะละเว้นการหยุดพัก หนึ่งคือกิจวัตรอื่น ๆ ห่างไกลจากมัน (ime) - และศักยภาพในการเข้าใจผิดและการควบคุมการไหลที่ไม่ตั้งใจนั้นยิ่งใหญ่กว่ามาก
quentin-starin

3
@ ใช่ใช่มันแตกต่างกัน แต่ทั้งคู่เป็นทางเลือก เขาถามเมื่อ / ว่าเหมาะสม ฉันให้ตัวอย่างของเมื่อมันจะ มันไม่ได้หมายถึงเป็นตัวอย่างที่รวมทุกอย่าง ทั้งนี้ขึ้นอยู่กับภาษาการมีงบก่อนการล้มอาจไม่ทำงาน ฉันไม่คิดว่ามันจะใช้กับ C # stackoverflow.com/questions/174155/…
Yatrix

8

ฉันเคยใช้มันเป็นครั้งคราวฉันคิดว่ามันเหมาะสมกับการใช้งานเสมอ - แต่เมื่อรวมกับความคิดเห็นที่เหมาะสม


7

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

ตัวอย่างเช่น (สังเกตความคิดเห็นที่อธิบาย):

public boolean isAvailable(Server server, HealthStatus health) {
  switch(health) {
    // Equivalent positive cases
    case HEALTHY:
    case UNDER_LOAD:
      return true;

    // Equivalent negative cases
    case FAULT_REPORTED:
    case UNKNOWN:
    case CANNOT_COMMUNICATE:
      return false;

    // Unknown enumeration!
    default:
      LOG.warn("Unknown enumeration " + health);
      return false;
  }
}

ฉันพบว่าการใช้งานประเภทนี้เป็นที่ยอมรับอย่างสมบูรณ์


ทราบว่ามีความแตกต่างใหญ่ระหว่างและcase X: case Y: doSomething() case X: doSomething(); case Y: doAnotherThing()ในครั้งแรกความตั้งใจค่อนข้างชัดเจนในขณะที่ในสองการตกผ่านอาจมีเจตนาหรือไม่ จากประสบการณ์ของฉันตัวอย่างแรกไม่เคยเรียกใช้คำเตือนใด ๆ ในคอมไพเลอร์ / ตัววิเคราะห์โค้ดในขณะที่คำสั่งที่สองทำ โดยส่วนตัวแล้วฉันจะเรียกตัวอย่างที่สองว่า "fall through" - ไม่แน่ใจเกี่ยวกับคำจำกัดความ "เป็นทางการ"
Jens Bannmann

6

ขึ้นอยู่กับ:

  • ความชอบส่วนตัวของคุณ
  • มาตรฐานการเข้ารหัสของนายจ้างของคุณ
  • ปริมาณความเสี่ยงที่เกี่ยวข้อง

ปัญหาหลักสองประการที่เกี่ยวข้องกับการอนุญาตให้เคสหนึ่งเคสผ่านไปยังเคสถัดไปคือ:

  1. มันทำให้รหัสของคุณขึ้นอยู่กับคำสั่งของคำสั่งกรณี นี่ไม่ใช่กรณีถ้าคุณไม่เคยผ่านและเพิ่มระดับของความซับซ้อนที่มักจะไม่พอใจ

  2. ไม่ชัดเจนว่ารหัสสำหรับหนึ่งกรณีรวมถึงรหัสสำหรับหนึ่งหรือหลายกรณีที่ตามมา

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


4

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

function get_julian_day (date) {
    int utc_date = date.getUTCDate();
    int utc_month = date.getUTCMonth();

    int julian_day = 0;

    switch (utc_month) {
        case 11: julian_day += 30;
        case 10: julian_day += 31;
        case 9:  julian_day += 30;
        case 8:  julian_day += 31;
        case 7:  julian_day += 31;
        case 6:  julian_day += 30;
        case 5:  julian_day += 31;
        case 4:  julian_day += 30;
        case 3:  julian_day += 31;
        case 2:  julian_day += 28;
        case 1:  julian_day += 31;
        default: break;
    }

    return julian_day + utc_date;
}

5
ในขณะที่นี่เป็นชนิดที่ฉลาดเวลาที่คุณประหยัดจากการทำเช่นนี้จะเป็นเรื่องที่เกินดุลตามเวลาที่คนอื่นจะคิดออกว่าสิ่งนี้ทำอะไร นอกจากนี้คุณสามารถเพิ่มประสิทธิภาพได้ดีขึ้นโดยการลบแง่มุมแบบหล่นผ่านเช่น 31 + 28 + 31 จะเป็น 90 เสมอหากคุณจะทำสิ่งนี้คุณอาจใส่ผลรวมในกรณีเนื่องจากมีเพียง 11 ข้อที่ต้องพิจารณา
JimmyJames

คุณพูดถูกตัวอย่างของฉันถูกประดิษฐ์ขึ้นอย่างแน่นอน :) ฉันจะบอกว่ามันง่ายกว่าที่จะสังเกตเห็นข้อผิดพลาดที่ซ่อนอยู่เมื่อจำนวนวันถูกสะกดเช่นนี้แทนการคำนวณล่วงหน้าทุกวันสะสมสำหรับแต่ละสวิตช์ .
AaronDanielson

2
ฉันจะให้คุณ แต่การทำเช่นนั้นในการประกาศคงที่จะดีกว่าเช่น FEB_START = 31; MAR_START = FEB_START + 28; ฯลฯ ไม่แน่ใจว่าภาษานี้เป็นอะไร แต่ในหลาย ๆ ภาษามันจะถูกคำนวณในเวลารวบรวม ปัญหาที่ใหญ่กว่าที่นี่คือว่ามันป้านเล็กน้อย ไม่มากที่มันเป็นความคิดที่ไม่ดีเพียงผิดปกติ เว้นแต่จะได้รับผลประโยชน์ที่ยิ่งใหญ่จริง ๆ โดยทั่วไปแล้วมันจะดีกว่าถ้าคุณใช้สำนวนสามัญ
JimmyJames

1
ทางเลือกที่ยอดเยี่ยมใช้ค่าคงที่สำหรับวันเริ่มต้นในตัวอย่างนี้ อย่างไรก็ตามประเด็นที่นี่ก็คือผลรวมสะสมเป็นการใช้ประโยชน์อย่างยิ่งสำหรับกรณีสวิตช์ล้ม ฉันเห็นด้วย 100% ในการใช้สำนวนทั่วไป แต่ลักษณะของคำถามนี้คือการใช้คุณลักษณะที่ลึกลับยิ่งขึ้นซึ่งสำนวนสามัญทั่วไปนั้นยากที่จะเกิดขึ้น
AaronDanielson

3

หากฉันรู้สึกว่าจำเป็นต้องเปลี่ยนจากกรณีหนึ่งไปอีกกรณีหนึ่ง (หายากเป็นที่ยอมรับ) ฉันชอบที่จะอธิบายอย่างชัดเจนและgoto caseแน่นอนว่าภาษาของคุณรองรับ

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

นอกจากนี้ยังช่วยหลีกเลี่ยงข้อบกพร่องที่อาจเกิดขึ้นเมื่อคำสั่ง case ถูกจัดลำดับใหม่

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