ควรเปลี่ยนคำสั่งที่มีข้อเริ่มต้นเสมอ?


254

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

  1. มีเหตุผลที่สมเหตุสมผลหรือไม่ในการรวมคำสั่งเริ่มต้นเสมอ?

  2. ภาษานี้ขึ้นอยู่กับอะไร? ฉันจำไม่ได้ว่าฉันใช้ภาษาอะไรในขณะนี้ - บางทีนี่อาจใช้กับบางภาษาและไม่ใช่กับคนอื่น ๆ ?


9
มันจะขึ้นอยู่กับภาษาในระดับสูง
skaffman

คำตอบ:


276

กรณีที่สวิทช์ควรเกือบมักจะมีdefaultกรณี

เหตุผลในการใช้ default

1. การ 'จับ' ค่าที่ไม่คาดคิด

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

2. เพื่อจัดการการกระทำ 'เริ่มต้น' โดยมีกรณีและปัญหาสำหรับพฤติกรรมพิเศษ

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

3. เพื่อแสดงให้คนที่อ่านรหัสของคุณทราบว่าคุณครอบคลุมกรณีดังกล่าว

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

นี่เป็นตัวอย่างที่ง่ายเกินไป แต่ประเด็นคือคนที่อ่านรหัสไม่ควรสงสัยว่าทำไมvariableไม่เป็นอย่างอื่นที่ไม่ใช่ 1 หรือ 2


กรณีเดียวที่ฉันคิดว่าไม่ควรใช้defaultคือเมื่อสวิตช์กำลังตรวจสอบบางสิ่งที่มันค่อนข้างชัดเจนว่าทุก ๆ ทางเลือกอื่นสามารถเพิกเฉยได้อย่างมีความสุข

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}

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

1
ในเวลาเดียวกันหากคุณกำลังตรวจสอบพารามิเตอร์ GET คุณอาจไม่ต้องการมีข้อผิดพลาดเกิดขึ้นทุกครั้งที่ผู้ใช้เปลี่ยน URL ที่ได้รับเป็นสิ่งที่ไม่ถูกต้อง: [
Andrew

4
@Andrew WASD เป็นเรื่องปกติสำหรับการเคลื่อนไหวของตัวละครในเกมคอมพิวเตอร์คุณไม่จำเป็นต้องดำเนินการใด ๆ เป็นค่าเริ่มต้นเนื่องจากสามารถกำหนดปุ่มอื่นให้กับการโต้ตอบอื่น ๆ ได้ ในสวิตช์ชนิดนี้มันดีที่จะเรียกฟังก์ชั่นการเคลื่อนไหว
jemiloii

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

3
@virusrocks หากเงื่อนไขของคุณเป็น enum คุณสามารถมีค่าที่ไม่คาดคิดได้ ตัวอย่างเช่นถ้าคุณเพิ่มค่าใหม่ให้กับ enum และลืมปรับปรุงสวิตช์ นอกจากนี้ในบางภาษาคุณสามารถกำหนดค่าจำนวนเต็มให้กับ enums ใน C ++ enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;ทำให้ถูกต้องMyEnumตัวอย่างที่ไม่ได้เท่ากับหรือFOO BARหากปัญหาเหล่านี้อย่างใดอย่างหนึ่งอาจทำให้เกิดข้อผิดพลาดในโครงการของคุณกรณีเริ่มต้นจะช่วยให้คุณค้นหาข้อผิดพลาดได้อย่างรวดเร็วบ่อยครั้งโดยไม่จำเป็นต้องดีบักเกอร์!
cdgraham

54

เลขที่

เกิดอะไรขึ้นถ้าไม่มีการกระทำเริ่มต้นบริบทสำคัญ จะทำอย่างไรถ้าคุณสนใจที่จะดำเนินการกับค่าเพียงเล็กน้อย

ยกตัวอย่างการอ่านปุ่มกดสำหรับเกม

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

เพิ่ม:

default: // Do nothing

เป็นเพียงการเสียเวลาและเพิ่มความซับซ้อนของรหัสโดยไม่มีเหตุผล


14
ตัวอย่างที่ดี แต่ในความเป็นจริงการเพิ่มส่วนคำสั่งเริ่มต้นด้วย// do nothingความคิดเห็นง่าย ๆทำให้ชัดเจนว่ามันเป็น 'ตกลง' ถ้าไม่ครอบคลุมทุกกรณีตรงข้ามกับคำสั่งสวิตช์อื่น ๆ ซึ่งจะเป็น 'ไม่ตกลง'
เล็บ

8
ไม่การเข้ารหัสไม่เพียงเกี่ยวกับการจัดการบางกรณี นอกจากนี้ยังทำหน้าที่เป็นเอกสาร โดยการเขียนค่าเริ่มต้น: และการแสดงความคิดเห็นเช่น // ไม่ทำสิ่งใดเลยความสามารถในการอ่านรหัสจะดีขึ้น
JongAm Park

22
ไม่เห็นด้วยกับความคิดเห็นอื่น การเพิ่มค่าเริ่มต้น // อย่าทำอะไรเพิ่มน้อยมากเว้นแต่คุณจะไม่รู้วิธีการทำงานของรหัส ฉันสามารถดูคำสั่ง switch โดยไม่มีค่าเริ่มต้นและรู้ว่าค่าเริ่มต้นคือการไม่ทำอะไรเลย การเพิ่มความยุ่งเหยิงไม่ทำให้สิ่งนี้ชัดเจนยิ่งขึ้น
Robert Noack

เผง มันอยู่ในแนวคิดเดียวกันกับการส่งอัลโพสต์พาราแรม คุณจัดการกับสิ่งที่คุณสนใจไม่ใช่คนอื่น
Jimmy Kane

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

45

ไม่มีกรณีเริ่มต้นจะมีประโยชน์จริง ๆ ในบางสถานการณ์

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

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }

นี่คือการสังเกตที่สำคัญ! มันใช้งานได้มากขึ้นใน Java เพราะมันไม่อนุญาตให้คุณใช้ ints เพื่อ enums
Lii

2
ในตัวอย่างของคุณคุณสามารถโยนข้อยกเว้นแทนที่จะกลับมาหลังจากคำสั่งเปลี่ยน ด้วยวิธีนี้คุณสามารถตรวจสอบทั้งคอมโบและคอมไพเลอร์และรับข้อผิดพลาดได้อย่างชัดเจนหากมีสิ่งที่ไม่คาดคิดเกิดขึ้น
Lii

1
ฉันเห็นด้วย. ในตัวอย่างของ Swift สวิตช์จะต้องหมดแรงไม่งั้นคุณจะได้รับ error compiler ให้ค่าเริ่มต้นเพียงปิดปากข้อผิดพลาดที่มีประโยชน์นี้ ในทางกลับกันการกำหนดค่าเริ่มต้นหากทุกกรณีได้รับการจัดการจริง ๆ แล้วโยนคำเตือนคอมไพเลอร์ (คำสั่งที่ไม่สามารถเข้าถึงได้)
Andy

1
เพิ่งแก้ไขข้อผิดพลาดที่จะทำให้เกิดการเตือนจากคอมไพเลอร์หากไม่มีค่าเริ่มต้น: ดังนั้นโดยทั่วไปแล้ว Switch over enum Variables ไม่ควรมีค่าเริ่มต้น
notan

42

ฉันจะใช้ประโยคเริ่มต้นเสมอไม่ว่าคุณจะใช้ภาษาอะไร

สิ่งที่สามารถและทำผิดไป ค่าจะไม่เป็นสิ่งที่คุณคาดหวังและอื่น ๆ

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

นั่นคือเหตุผลที่คุณควรใช้ประโยคเริ่มต้นและข้อผิดพลาดตัวอย่างเช่นใน Java:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

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


39
ฉันต้องการthrow new RuntimeException("myVar invalid " + myVar);ตั้งแต่ที่อาจให้ข้อมูลเพียงพอที่จะคิดออกและแก้ไขข้อผิดพลาดโดยไม่ต้องใช้ดีบักเกอร์และตรวจสอบตัวแปรซึ่งอาจเป็นเรื่องยากหากนี่เป็นปัญหาที่เกิดขึ้นยากที่จะทำซ้ำ
Chris Dodd

1
เกิดอะไรขึ้นถ้ามันไม่ใช่เงื่อนไขข้อผิดพลาดสำหรับค่าที่ไม่ตรงกับหนึ่งในกรณี?
Gabe

4
default:ถ้ามันไม่ได้เป็นเงื่อนไขข้อผิดพลาดแล้วมันน่าจะมีความจำเป็นในการไม่มี แต่บ่อยครั้งที่ไม่มีใครคิดว่าเป็นค่าเริ่มต้นเพราะไม่มีใครคิดว่าmyVarจะมีค่าอื่นใดนอกเหนือจากที่ระบุไว้; แต่ฉันไม่สามารถนับจำนวนครั้งที่ฉันประหลาดใจในชีวิตจริงเมื่อตัวแปรมีค่านอกเหนือจากค่าที่ "อาจเป็นไปได้" ในกรณีเหล่านั้นฉันรู้สึกขอบคุณสำหรับข้อยกเว้นทำให้ฉันเห็นว่าทันทีแทนที่จะทำให้เกิดข้อผิดพลาดอื่น ๆ ในภายหลัง (ยากต่อการแก้ไขข้อผิดพลาด) หรือคำตอบที่ผิด (อาจถูกมองข้ามในการทดสอบ)
Adrian Smith

2
ฉันเห็นด้วยกับเอเดรีย ล้มเหลวอย่างหนักและล้มเหลวในช่วงต้น
Slappy

ฉันชอบการโยนAssertionErrorแทนRuntimeExceptionเนื่องจากสถานการณ์นี้คล้ายกับการยืนยันที่ระบุว่าmyVarเป็นหนึ่งในค่าที่จัดการ นอกจากนี้โอกาสที่บางคนจะจับและกลืนกินข้อผิดพลาดก็เล็กลง
Philipp Wendler

15

ใน บริษัท ของฉันเราเขียนซอฟต์แวร์สำหรับตลาด Avionics และการป้องกันและเรามักจะมีคำสั่งเริ่มต้นเพราะทุกกรณีในคำสั่งเปลี่ยนจะต้องได้รับการจัดการอย่างชัดเจน (แม้ว่ามันจะเป็นเพียงความคิดเห็นว่า 'ไม่ทำอะไรเลย') เราไม่สามารถจ่ายซอฟต์แวร์เพียงเพื่อประพฤติผิดหรือล้มเหลวในค่าที่ไม่คาดคิด (หรือแม้แต่สิ่งที่เราคิดว่าเป็นไปไม่ได้)

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


1
ฉันมีข้อกำหนดเดียวกันกับที่ทำงาน เราเขียนรหัสสำหรับตัวควบคุมแบบฝังตัวที่ต้องผ่านการตรวจสอบความปลอดภัยอย่างเข้มงวดและมักจะอยู่ภายใต้ EMI มันจะไม่รับผิดชอบที่จะสมมติว่าตัวแปรที่แจกแจงจะไม่มีค่าที่ไม่ได้อยู่ในรายการการแจงนับ
oosterwal

12

คำสั่ง "switch" ควรมีประโยคเริ่มต้นเสมอหรือไม่? ไม่ปกติแล้วควรรวมค่าเริ่มต้นด้วย

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

นี่เป็นตัวอย่างเล็กน้อยที่มันไม่มีเหตุผล:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

นี่คือเทียบเท่า:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}

ฉันไม่เห็นด้วย. IMHO ครั้งเดียวที่ไม่ควรมีค่าเริ่มต้นคือถ้าไม่มีทางเป็นครั้งคราวที่ชุดของอินพุตสามารถเปลี่ยนแปลงได้และคุณจะได้รับค่าที่เป็นไปได้ทั้งหมด กรณีที่ง่ายที่สุดที่ฉันคิดได้คือค่าบูลีนจากฐานข้อมูลโดยที่คำตอบเดียว (จนกระทั่งการเปลี่ยนแปลง SQL!) เป็นจริงเท็จและเป็นโมฆะ ในอีกกรณีหนึ่งการมีค่าเริ่มต้นด้วยการยืนยันหรือยกเว้น "ไม่ควรเกิดขึ้น!" ทำให้รู้สึกดี หากรหัสของคุณเปลี่ยนแปลงและคุณมีค่าใหม่หนึ่งค่าหรือมากกว่านั้นคุณสามารถมั่นใจได้ว่าคุณมีรหัสสำหรับพวกเขาและการทดสอบของคุณจะระเบิดหากคุณไม่ได้
Harper Shelby

4
@Harper: ตัวอย่างของคุณอยู่ในหมวดหมู่ของ "ยืนยันเงื่อนไขข้อผิดพลาด"
Gabe

ฉันยืนยันว่าตัวอย่างของฉันเป็นบรรทัดฐานและมีจำนวนคดีเล็ก ๆ ที่สามารถมั่นใจได้ 100% ทุกกรณีที่เป็นไปได้ครอบคลุมและค่าเริ่มต้นไม่จำเป็นต้องมีข้อยกเว้น คำตอบของคุณคือ worded ในแบบที่ทำให้ฟังดู (อย่างน้อยสำหรับฉัน) ราวกับว่าสิ่งที่ไม่มีค่าเริ่มต้นจะเกิดขึ้นบ่อยกว่าไม่
Harper Shelby

@Harper: ตกลงฉันเปลี่ยนข้อความเพื่อระบุว่าสถานการณ์ที่ไม่ทำอะไรเป็นเรื่องธรรมดาน้อย
Gabe

4
ฉันคิดว่าdefault:ในกรณีเหล่านี้ประมวลข้อสมมติฐานของคุณ ตัวอย่างเช่นเมื่อsgn==0พิมพ์แล้วinteger(ไม่ใช่ทั้งบวกและลบ) หรือว่าเป็นข้อผิดพลาดหรือไม่? สำหรับฉันที่อ่านรหัสนั้นมันยากที่จะพูด ฉันสมมติว่าคุณต้องการเขียนzeroในกรณีนี้ไม่ใช่integerและโปรแกรมเมอร์ทำข้อสันนิษฐานที่sgnสามารถเป็น -1 หรือ +1 เท่านั้น หากเป็นเช่นนั้นการมีโปรแกรมdefault:จะอนุญาตให้โปรแกรมเมอร์จับข้อผิดพลาดของสมมติฐานก่อนและเปลี่ยนรหัสได้
Adrian Smith

7

เท่าที่ฉันเห็นคำตอบคือ 'default' เป็นทางเลือกการบอกสวิตช์ต้องมีค่าเริ่มต้นเสมอเช่นการพูดว่า 'if-elseif' ทุกครั้งต้องมี 'else' หากมีตรรกะที่ต้องทำตามค่าเริ่มต้นคำสั่ง 'default' ควรอยู่ที่นั่น แต่มิฉะนั้นรหัสสามารถดำเนินการต่อโดยไม่ต้องทำอะไรเลย


6

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

ดังนั้นฉันเชื่อว่าหากไม่สามารถเข้าถึงค่าเริ่มต้น - คุณไม่จำเป็นต้องเพิ่ม

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


3
อีกสาเหตุทั่วไปที่ทำให้เกิดกรณีที่ไม่คาดคิด: ส่วนอื่น ๆ ของรหัสได้รับการแก้ไขบางทีอาจจะเป็นปีต่อมา
Hendrik Brummermann

อันที่จริงนี่เป็นพฤติกรรมที่เป็นอันตรายมาก อย่างน้อยฉันจะเพิ่มข้อยืนยัน () จากนั้นจะตรวจจับได้ง่ายเมื่อใดก็ตามที่มีข้อผิดพลาด
Kurt Pattyn

5

ฉันจะบอกว่ามันขึ้นอยู่กับภาษา แต่ใน C หากคุณเปลี่ยนเป็น enum type และคุณจัดการทุกค่าที่เป็นไปได้คุณน่าจะดีกว่าไม่รวมถึงกรณีเริ่มต้น ด้วยวิธีนี้ถ้าคุณเพิ่มแท็ก enum เพิ่มเติมในภายหลังและลืมเพิ่มลงในสวิตช์คอมไพเลอร์ที่เชี่ยวชาญจะให้คำเตือนเกี่ยวกับกรณีที่หายไป


5
"ทุกค่าที่เป็นไปได้" ไม่น่าจะถูกจัดการด้วย enums คุณสามารถส่งจำนวนเต็มไปยัง enum แม้ว่าค่าเฉพาะนั้นจะไม่ได้กำหนดไว้อย่างชัดเจน และคอมไพเลอร์ตัวไหนที่เตือนเรื่องเคสที่หายไป?
TrueWill

1
@TrueWill: ใช่คุณสามารถใช้ cast ที่ชัดเจนเพื่อเขียนโค้ดที่สับสนซึ่งเป็นไปไม่ได้ที่จะเข้าใจ ในการเขียนโค้ดที่เข้าใจได้คุณควรหลีกเลี่ยง gcc -Wall(เรียงลำดับตัวหารร่วมที่ต่ำที่สุดของคอมไพเลอร์ที่เชี่ยวชาญ) ให้คำเตือนเกี่ยวกับ enums ที่ไม่ได้จัดการในคำสั่งสวิตช์
Chris Dodd

4

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

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}

ลำไส้ใหญ่defaultยังคงต้องการหลังจากที่นี่หรือไม่ หรือการทำเช่นนี้อนุญาตให้มีไวยากรณ์พิเศษบางอย่างที่อนุญาตให้คุณละเว้นได้หรือไม่
Ponkadoodle

จำเป็นต้องใช้โคลอนนั่นเป็นตัวพิมพ์ผิดขอขอบคุณที่แจ้งให้ฉันทราบ
deegee

3

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

private static void switch1(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        break;
    case "Tuesday":
        System.out.println("Tuesday");
        break;
    }
}

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

    private static String switch2(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        return name;

    case "Tuesday":
        System.out.println("Tuesday");
        return name;

    default:
        return name;
    }
}

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


3

หลักเกณฑ์บางอย่าง (ล้าสมัย) กล่าวเช่นMISRA C :

ข้อกำหนดสำหรับข้อเริ่มต้นสุดท้ายคือการเขียนโปรแกรมป้องกัน ข้อนี้จะต้องดำเนินการที่เหมาะสมหรือมีความคิดเห็นที่เหมาะสมว่าทำไมไม่มีการดำเนินการ

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

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

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

ความต้องการทั่วไปของการตรวจสอบแบบไดนามิกคือการจัดการอินพุตในมุมกว้าง หากค่านั้นมาจากนอกการควบคุมของโปรแกรมจะไม่สามารถเชื่อถือได้

นี่คือจุดที่ Misra รับจุดยืนของการตั้งโปรแกรมการป้องกันขั้นสูงซึ่งตราบใดที่ค่าที่ไม่ถูกต้องนั้นสามารถนำไปใช้แทนได้จริงจะต้องตรวจสอบด้วยไม่ว่าโปรแกรมนั้นจะถูกต้องหรือไม่ก็ตาม ซึ่งทำให้รู้สึกว่าซอฟต์แวร์จำเป็นต้องเชื่อถือได้มากที่สุดเท่าที่จะเป็นไปได้เมื่อมีข้อผิดพลาดของฮาร์ดแวร์ แต่ดังที่ Ophir Yoktan กล่าวว่าซอฟต์แวร์ส่วนใหญ่ดีกว่าไม่ใช่ "การจัดการ" ข้อบกพร่อง การปฏิบัติหลังบางครั้งเรียกว่าการเขียนโปรแกรมเป็นที่น่ารังเกียจ


2

คุณควรมีค่าเริ่มต้นให้จับค่าที่ไม่คาดคิดเข้ามา

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

ในกรณีที่คุณมี BSOD ไร้ความหมายอย่างที่สุดกี่ครั้ง? หรือมีข้อยกเว้นร้ายแรง @ 0x352FBB3C32342


เช่น - จำไว้ว่ามันไม่ใช่แค่นักพัฒนาซอฟต์แวร์ที่เห็นข้อความผิดพลาดเสมอ เราอยู่ในโลกแห่งความเป็นจริงผู้คนทำผิดพลาด
John Hunt

2

หากค่าสวิตช์ ( สวิตช์ (ตัวแปร )) ไม่สามารถเข้าถึงตัวพิมพ์เริ่มต้นได้แสดงว่าตัวพิมพ์เริ่มต้นนั้นไม่จำเป็นเลย แม้ว่าเราจะรักษากรณีเริ่มต้น แต่ก็ไม่ได้ดำเนินการเลย มันเป็นรหัสตาย


2

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

หากความเป็นไปได้ของคดีมี จำกัด (เช่นบูลีน) ดังนั้นประโยคเริ่มต้นจะซ้ำซ้อน !


2

หากไม่มีกรณีเริ่มต้นในswitchคำสั่งพฤติกรรมสามารถคาดเดาไม่ได้ถ้ากรณีนั้นเกิดขึ้นในบางช่วงเวลาซึ่งไม่สามารถคาดการณ์ได้ในขั้นตอนการพัฒนา เป็นวิธีปฏิบัติที่ดีในการรวมdefaultกรณีและปัญหา

switch ( x ){
  case 0 : { - - - -}
  case 1 : { - - - -}
}

/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */

การปฏิบัติดังกล่าวสามารถส่งผลให้เกิดข้อผิดพลาดเช่นdereference โมฆะ , หน่วยความจำรั่วไหลเช่นเดียวกับประเภทอื่น ๆ ของ ข้อบกพร่องร้ายแรง

ตัวอย่างเช่นเราสมมติว่าแต่ละเงื่อนไขเริ่มต้นตัวชี้ แต่ถ้าdefaultกรณีควรจะเกิดขึ้นและถ้าเราไม่ได้เริ่มต้นในกรณีนี้ก็มีความเป็นไปได้ของการลงจอดด้วยข้อยกเว้นตัวชี้โมฆะ ดังนั้นจึงแนะนำให้ใช้defaultคำสั่ง case แม้ว่ามันอาจจะไม่สำคัญก็ตาม


2

กรณีเริ่มต้นอาจไม่จำเป็นในสวิตช์ที่ใช้โดย enum เมื่อสวิตช์มีค่าทั้งหมดกรณีเริ่มต้นจะไม่ดำเนินการ ดังนั้นในกรณีนี้มันไม่จำเป็น


1

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


1

ฉันไม่เห็นด้วยกับคำตอบที่ได้รับการโหวตมากที่สุดของ Vanwaril ด้านบน

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

  1. งบสวิตช์ Nonexhaustive:ผู้ที่ควรเสมอมีค่าเริ่มต้น ตามชื่อแนะนำสิ่งเหล่านั้นเป็นข้อความที่ไม่ครอบคลุมค่าที่เป็นไปได้ทั้งหมด สิ่งนี้อาจเป็นไปไม่ได้เช่นคำสั่ง switch ในค่าจำนวนเต็มหรือบน String ที่นี่ฉันต้องการใช้ตัวอย่างของ Vanwaril (ควรกล่าวว่าฉันคิดว่าเขาใช้ตัวอย่างนี้เพื่อให้คำแนะนำที่ไม่ถูกต้องฉันใช้ที่นี่เพื่อระบุคำตรงกันข้าม -> ใช้ข้อความเริ่มต้น):

    switch(keystroke)
    {
      case 'w':
        // move up
      case 'a':
        // move left
      case 's':
        // move down
      case 'd':
        // move right
      default:          
        // cover all other values of the non-exhaustive switch statement
    }
    

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

  2. คำสั่งสวิตช์แบบหมดจด: คำสั่งสวิตช์เหล่านั้นครอบคลุมค่าที่เป็นไปได้ทั้งหมดเช่นคำสั่งสวิตช์บนการแจงนับประเภทระบบเกรด เมื่อพัฒนารหัสในครั้งแรกมันง่ายที่จะครอบคลุมค่าทั้งหมด อย่างไรก็ตามในขณะที่เราเป็นมนุษย์มีโอกาสเล็กน้อยที่จะลืมบางอย่าง นอกจากนี้หากคุณเพิ่มค่า enum ในภายหลังเช่นนั้นคำสั่ง switch ทั้งหมดจะต้องถูกปรับเปลี่ยนเพื่อให้หมดจดอีกครั้งเปิดพา ธ ไปยังนรก ทางออกที่ง่ายคือเครื่องมือวิเคราะห์รหัสแบบคงที่ เครื่องมือควรตรวจสอบคำสั่ง switch ทั้งหมดและตรวจสอบว่าหมดแรงหรือมีค่าเริ่มต้นหรือไม่ นี่คือตัวอย่างสำหรับคำสั่งสวิตช์แบบละเอียด ก่อนอื่นเราต้องมี enum:

    public enum GradeSystemType {System1To6, SystemAToD, System0To100}
    

    จากนั้นเราต้องการตัวแปรของ enum เช่นGradeSystemType type = ...นี้ คำสั่งสวิตช์แบบละเอียดจะมีลักษณะดังนี้:

    switch(type)
    {
      case GradeSystemType.System1To6:
        // do something
      case GradeSystemType.SystemAToD:
        // do something
      case GradeSystemType.System0To100:
        // do something
    }
    

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

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


0

ควรเปลี่ยนคำสั่งที่มีข้อเริ่มต้นเสมอ? ไม่มีกรณีสวิทช์ที่มีอยู่โดยไม่ใช้กรณีเริ่มต้นในกรณีที่กรณีสวิทช์เริ่มต้นจะเรียกค่าสวิทช์switch(x)ในกรณีนี้ x เมื่อไม่ตรงกับค่ากรณีอื่น ๆ


0

ฉันเชื่อว่านี่เป็นภาษาที่เฉพาะเจาะจงและสำหรับกรณี C ++ เป็นจุดรองสำหรับประเภทคลาส Enum ซึ่งปรากฏว่าปลอดภัยกว่า C Enum แบบดั้งเดิม แต่

หากคุณดูที่การใช้ std :: byte มันคล้ายกับ:

enum class byte : unsigned char {} ;

ที่มา: https://en.cppreference.com/w/cpp/language/enum

และพิจารณาสิ่งนี้ด้วย:

มิฉะนั้นถ้า T เป็นประเภทการแจงนับที่มีการกำหนดขอบเขตหรือ unscoped กับประเภทพื้นฐานคงที่และถ้า braced-init-list มีเพียง initializer เดียวเท่านั้นและหากการแปลงจาก initializer เป็นประเภทพื้นฐานนั้นจะไม่แคบลงและถ้า การกำหนดค่าเริ่มต้นคือรายการโดยตรงเริ่มต้นจากนั้นการแจงนับจะเริ่มต้นได้ด้วยผลลัพธ์ของการแปลงค่าเริ่มต้นให้เป็นชนิดพื้นฐาน

(ตั้งแต่ C ++ 17)

ที่มา: https://en.cppreference.com/w/cpp/language/list_initialization

นี่คือตัวอย่างของคลาส enum ที่แสดงค่าที่ไม่ได้กำหนดตัวแจงนับ ด้วยเหตุนี้คุณจึงไม่สามารถไว้วางใจใน enums อาจมีความสำคัญ

อย่างไรก็ตามฉันชอบสิ่งที่ @Harlan Kassler พูดในโพสต์ของเขาและจะเริ่มใช้กลยุทธ์นั้นในบางสถานการณ์ด้วยตัวเอง

เป็นเพียงตัวอย่างของคลาส Enum ที่ไม่ปลอดภัย:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

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