สลับคำสั่งผิดพลาดใน C # หรือไม่


365

การเปลี่ยนคำสั่งผิดพลาดเป็นหนึ่งในเหตุผลสำคัญที่ทำให้ฉันรักswitchกับการif/else ifสร้าง ตัวอย่างอยู่ในลำดับที่นี่:

static string NumberToWords(int number)
{
    string[] numbers = new string[] 
        { "", "one", "two", "three", "four", "five", 
          "six", "seven", "eight", "nine" };
    string[] tens = new string[] 
        { "", "", "twenty", "thirty", "forty", "fifty", 
          "sixty", "seventy", "eighty", "ninety" };
    string[] teens = new string[]
        { "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
          "sixteen", "seventeen", "eighteen", "nineteen" };

    string ans = "";
    switch (number.ToString().Length)
    {
        case 3:
            ans += string.Format("{0} hundred and ", numbers[number / 100]);
        case 2:
            int t = (number / 10) % 10;
            if (t == 1)
            {
                ans += teens[number % 10];
                break;
            }
            else if (t > 1)
                ans += string.Format("{0}-", tens[t]);
        case 1:
            int o = number % 10;
            ans += numbers[o];

            break;
        default:
            throw new ArgumentException("number");
    }
    return ans;
}

คนฉลาดกำลังประจบประแจงเพราะstring[]s ควรถูกประกาศนอกฟังก์ชั่น: เอ่อพวกเขานี่เป็นเพียงตัวอย่าง

คอมไพเลอร์ล้มเหลวโดยมีข้อผิดพลาดดังต่อไปนี้:

การควบคุมไม่สามารถผ่านจากป้ายกำกับกรณีหนึ่ง ('กรณีที่ 3:') ไปยังป้ายกำกับอื่น
การควบคุมไม่สามารถผ่านจากป้ายกำกับกรณีหนึ่ง ('กรณีที่ 2:') ไปยังป้ายกำกับอื่น

ทำไม? และมีวิธีใดที่จะทำให้เกิดพฤติกรรมแบบนี้โดยไม่ต้องมีสามตัวif?

คำตอบ:


648

(คัดลอก / วางคำตอบที่ฉันให้ไว้ที่อื่น )

ตกผ่านswitch- cases สามารถทำได้โดยมีรหัสไม่มีในcase(ดูcase 0) หรือใช้พิเศษgoto case(ดูcase 1) หรือgoto default(ดูcase 2) รูปแบบ:

switch (/*...*/) {
    case 0: // shares the exact same code as case 1
    case 1:
        // do something
        goto case 2;
    case 2:
        // do something else
        goto default;
    default:
        // do something entirely different
        break;
}

121
ฉันคิดว่าในกรณีนี้ข้ามไปไม่ถือว่าเป็นอันตราย
Thomas Owens

78
ประณาม - ฉันเคยเขียนโปรแกรมกับ C # ตั้งแต่วันแรกที่ 1.0 และฉันไม่เคยเห็นสิ่งนี้มาก่อนจนกระทั่งตอนนี้ เพิ่งไปแสดงคุณเรียนรู้สิ่งใหม่ทุกวัน
Erik Forbes

37
ทุกอย่างดีเอริค เหตุผลเดียว / ฉัน / รู้เกี่ยวกับมันก็คือว่าฉันเป็นทฤษฎีเรียบเรียง nerd ผู้อ่านรายละเอียด ECMA-334 ด้วยแว่นขยาย
Alex Lyman

13
@Dancrumb: ในขณะที่เขียนคุณสมบัติ C # ยังไม่ได้เพิ่มคำหลัก "อ่อน" ใด ๆ (เช่น 'ผลผลิต', 'var', 'จาก' และ 'เลือก') ดังนั้นพวกเขาจึงมีสามตัวเลือกจริง: 1 ) ทำให้ 'fallthrough' เป็นคำหลักที่ยาก (คุณไม่สามารถใช้มันเป็นชื่อตัวแปร), 2) เขียนโค้ดที่จำเป็นเพื่อสนับสนุนคำหลักที่อ่อนนุ่มเช่นนี้ 3) ใช้คำหลักที่สงวนไว้แล้ว # 1 เป็นปัญหาใหญ่สำหรับรหัสพอร์ตเหล่านั้น # 2 เป็นงานวิศวกรรมที่ค่อนข้างใหญ่จากสิ่งที่ฉันเข้าใจ และตัวเลือกที่พวกเขาไปด้วย # 3 มีประโยชน์ด้าน: devs อื่น ๆ อ่านรหัสหลังจากความจริงสามารถเรียนรู้เกี่ยวกับคุณลักษณะจากแนวคิดพื้นฐานของ goto
Alex Lyman

10
ทั้งหมดนี้พูดคุยเกี่ยวกับคำหลักใหม่ / พิเศษสำหรับการเข้าใจผิดอย่างชัดเจน พวกเขาไม่สามารถใช้คำหลัก 'ดำเนินการต่อ' ใช่หรือไม่ ในคำอื่น ๆ แยกออกจากสวิตช์หรือดำเนินการต่อในกรณีต่อไป (ล้มลง)
Tal Even-Tov

44

"ทำไม" คือการหลีกเลี่ยงการล้มโดยบังเอิญซึ่งฉันรู้สึกซาบซึ้ง นี่เป็นแหล่งที่มาของข้อบกพร่องใน C และ Java

วิธีแก้ปัญหาคือใช้ข้ามไปเช่น

switch (number.ToString().Length)
{
    case 3:
        ans += string.Format("{0} hundred and ", numbers[number / 100]);
        goto case 2;
    case 2:
    // Etc
}

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


1
นี่คือความแตกต่างระหว่างสวิตช์และหาก / elseif ในใจของฉัน Switch ใช้สำหรับตรวจสอบสถานะต่าง ๆ ของตัวแปรเดี่ยวในขณะที่ถ้า / elseif สามารถใช้เพื่อตรวจสอบจำนวนของสิ่งต่าง ๆ ที่เชื่อมต่อ แต่ไม่จำเป็นต้องเป็นตัวแปรเดียวหรือตัวแปรเดียวกัน
Matthew Scharley

2
ถ้าเป็นการป้องกันโดยไม่ตั้งใจให้ตกผมก็รู้สึกว่าคำเตือนของคอมไพเลอร์น่าจะดีกว่า เช่นเดียวกับคุณมีหนึ่งถ้าคำสั่ง if ของคุณมีการมอบหมาย:if (result = true) { }
Tal Even-Tov

3
@ TalEven-Tov: คำเตือนของคอมไพเลอร์ควรเป็นกรณีที่คุณสามารถแก้ไขรหัสให้ดีขึ้นได้ โดยส่วนตัวแล้วฉันชอบการทำลายโดยปริยายดังนั้นมันจะไม่เป็นปัญหาสำหรับการเริ่มต้น แต่นั่นเป็นเรื่องที่แตกต่าง
Jon Skeet

26

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

switch(value)
{
    case 1:// this is still legal
    case 2:
}

23
ฉันไม่เคยเข้าใจว่าเหตุใดจึงไม่ใช่ "กรณีที่ 1, 2:"
BCS

11
@ David Pfeffer: ใช่แล้วและเป็นcase 1, 2:ภาษาที่อนุญาต สิ่งที่ฉันจะไม่เข้าใจคือเหตุผลว่าทำไมภาษาสมัยใหม่ใด ๆ จึงไม่เลือกที่จะอนุญาต
BCS

@BCS พร้อมกับคำสั่ง goto ตัวเลือกคั่นด้วยเครื่องหมายจุลภาคหลายตัวอาจเป็นเรื่องยุ่งยากในการจัดการ?
penguat

@pengut: มันอาจจะแม่นยำมากกว่าที่จะบอกว่าcase 1, 2:เป็นป้ายกำกับเดียว แต่มีหลายชื่อ - FWIW ฉันคิดว่าภาษาส่วนใหญ่ที่ห้ามไม่ให้ใช้อินเดียมกรณีพิเศษคือ "อินเดียมป้ายกำกับต่อเนื่อง" แต่ให้ถือป้ายกำกับเคสเป็นหมายเหตุประกอบในคำสั่งถัดไปและต้องการคำสั่งสุดท้ายก่อนที่จะมีข้อความกำกับ (หรือ มากขึ้น) ฉลากเคสจะกระโดด
BCS

22

เพื่อเพิ่มคำตอบที่นี่ฉันคิดว่ามันมีมูลค่าการพิจารณาคำถามตรงข้ามร่วมกับนี้ ได้แก่ เหตุใด C อนุญาตให้ล้มลงตั้งแต่แรก

หลักสูตรการเขียนโปรแกรมภาษาใด ๆ ให้บริการสองเป้าหมาย:

  1. ให้คำแนะนำกับคอมพิวเตอร์
  2. ออกจากบันทึกของความตั้งใจของโปรแกรมเมอร์

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

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

ตอนนี้switchสามารถคอมไพล์ได้เสมอโดยแปลงเป็นเชนเทียบเท่าif-elseบล็อกหรือคล้ายกัน แต่มันถูกออกแบบมาเพื่อให้คอมไพล์ในรูปแบบแอสเซมบลีทั่วไปโดยเฉพาะที่หนึ่งใช้ค่าคำนวณออฟเซ็ตจากมัน (ไม่ว่าจะมองตาราง จัดทำดัชนีโดยแฮชที่สมบูรณ์แบบของค่าหรือตามจำนวนจริงในค่า *) เป็นที่น่าสังเกตว่า ณ จุดนี้ในวันนี้การรวบรวม C # บางครั้งจะกลายswitchเป็นสิ่งที่เทียบเท่าif-elseและบางครั้งใช้วิธีการกระโดดแบบแฮชอิง (และเช่นเดียวกันกับ C, C ++ และภาษาอื่นที่มีไวยากรณ์เปรียบเทียบ)

ในกรณีนี้มีสองเหตุผลที่ดีในการอนุญาตให้ล้มลง:

  1. มันเกิดขึ้นเองตามธรรมชาติอยู่แล้ว: ถ้าคุณสร้างตารางการกระโดดเป็นชุดของคำสั่งและหนึ่งในชุดคำสั่งก่อนหน้านี้ไม่มีการกระโดดหรือกลับการเรียงลำดับบางอย่าง การอนุญาตให้ล้มลงคือสิ่งที่จะ "เพิ่งเกิดขึ้น" หากคุณเปลี่ยนswitch-using C ให้เป็นตารางกระโดดโดยใช้รหัสเครื่อง

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

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

สี่ทศวรรษต่อมาแม้ว่าสิ่งต่าง ๆ จะไม่เหมือนกันด้วยเหตุผลบางประการ:

  1. โคเดกใน C วันนี้อาจมีประสบการณ์การประกอบน้อยหรือไม่มีเลย ผู้เขียนโค้ดในภาษา C-style อื่น ๆ มีแนวโน้มที่จะน้อยลง (โดยเฉพาะ Javascript!) แนวคิดของ "สิ่งที่ผู้คนคุ้นเคยจากการชุมนุม" ไม่เกี่ยวข้องอีกต่อไป
  2. การปรับปรุงในการปรับให้เหมาะสมหมายความว่าโอกาสที่switchจะกลายเป็นif-elseเพราะมันถือว่าเป็นแนวทางที่น่าจะมีประสิทธิภาพมากที่สุดหรืออื่น ๆ กลายเป็นตัวแปรที่เป็นความลับโดยเฉพาะอย่างยิ่งของวิธีการกระโดดตารางที่สูงขึ้น การทำแผนที่ระหว่างวิธีการระดับสูงและระดับล่างไม่แข็งแรงเท่าที่เคยเป็นมา
  3. ประสบการณ์แสดงให้เห็นว่าการตกผ่านมีแนวโน้มที่จะเป็นกรณีของชนกลุ่มน้อยมากกว่าปกติ (การศึกษาคอมไพเลอร์ของซันพบว่า 3% ของswitchบล็อกใช้การหล่นผ่านนอกเหนือจากฉลากหลาย ๆ ตัวในบล็อกเดียวกันและคิดว่าการใช้งาน - กรณีที่นี่หมายความว่า 3% นี้เป็นจริงสูงกว่าปกติ) ดังนั้นภาษาที่ศึกษาทำให้ผิดปกติได้ง่ายกว่ามาก
  4. ประสบการณ์ได้แสดงให้เห็นว่าการล้มผ่านมีแนวโน้มที่จะเป็นแหล่งที่มาของปัญหาทั้งในกรณีที่มีการทำโดยไม่ตั้งใจและในกรณีที่มีคนพลาดการแก้ไขรหัสผ่านที่ถูกต้อง สิ่งหลังนี้เป็นส่วนเพิ่มเติมที่ละเอียดอ่อนของข้อบกพร่องที่เกี่ยวข้องกับการร่วงผ่านเนื่องจากถึงแม้ว่ารหัสของคุณจะไม่มีข้อบกพร่องอย่างสมบูรณ์แบบการตกผ่านของคุณยังสามารถทำให้เกิดปัญหาได้

เกี่ยวข้องกับจุดสองจุดสุดท้ายเหล่านั้นให้พิจารณาข้อความต่อไปนี้จาก K&R ฉบับปัจจุบัน:

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

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

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

และเมื่อคุณคิดถึงมันรหัสเช่นนี้:

switch(x)
{
  case 1:
   foo();
   /* FALLTHRU */
  case 2:
    bar();
    break;
}

กำลังเพิ่มบางสิ่งเพื่อทำให้การล่มสลายชัดเจนในโค้ดไม่ใช่สิ่งที่สามารถตรวจพบได้ (โดยที่คอมไพเลอร์ตรวจพบ

ดังนั้นความจริงที่ว่าจะต้องมีความชัดเจนในการล้มใน C # ไม่ได้เพิ่มโทษให้กับคนที่เขียนได้ดีในภาษา C สไตล์อื่น ๆ ต่อไปเนื่องจากพวกเขาจะมีความชัดเจนใน fall-throughs ของพวกเขาแล้ว†

ในที่สุดการใช้gotoที่นี่เป็นบรรทัดฐานจาก C และภาษาอื่น ๆ :

switch(x)
{
  case 0:
  case 1:
  case 2:
    foo();
    goto below_six;
  case 3:
    bar();
    goto below_six;
  case 4:
    baz();
    /* FALLTHRU */
  case 5:
  below_six:
    qux();
    break;
  default:
    quux();
}

ในกรณีเช่นนี้ที่เราต้องการให้บล็อกรวมอยู่ในโค้ดที่ดำเนินการสำหรับค่าอื่นนอกเหนือจากที่นำมาหนึ่งบล็อกก่อนหน้านี้แล้วเราต้องใช้gotoแล้ว (แน่นอนว่ามีวิธีการและวิธีการหลีกเลี่ยงปัญหานี้ด้วยเงื่อนไขที่แตกต่างกัน แต่นั่นเป็นเรื่องจริงสำหรับทุกสิ่งที่เกี่ยวข้องกับคำถามนี้) เช่น C # ที่สร้างขึ้นด้วยวิธีปกติแล้วจัดการกับสถานการณ์หนึ่งที่เราต้องการกดรหัสมากกว่าหนึ่งบล็อกใน a switchและเพียงแค่วางให้ครอบคลุมถึง fall-through เช่นกัน นอกจากนี้ยังทำให้ทั้งสองกรณีสะดวกและจัดทำเอกสารด้วยตนเองเนื่องจากเราต้องเพิ่มป้ายกำกับใหม่ใน C แต่สามารถใช้caseเป็นป้ายกำกับใน C # ได้ ใน C # เราสามารถกำจัดbelow_sixฉลากและใช้goto case 5ที่ชัดเจนว่าเรากำลังทำอะไร (เราต้องเพิ่มด้วยbreakสำหรับdefault, ซึ่งฉันทิ้งไว้เพียงเพื่อให้รหัส C ด้านบนไม่ชัดเจนรหัส C #)

โดยสรุปแล้ว:

  1. C # ไม่เกี่ยวข้องกับเอาท์พุทคอมไพเลอร์ที่ไม่ได้เพิ่มประสิทธิภาพเนื่องจากรหัส C ทำเมื่อ 40 ปีที่แล้ว (หรือ C วันนี้) ซึ่งทำให้แรงบันดาลใจของการตกที่ไม่เกี่ยวข้อง
  2. C # ยังคงเข้ากันได้กับ C ไม่ใช่แค่มีนัยbreakเพื่อการเรียนรู้ภาษาที่ง่ายขึ้นโดยผู้ที่คุ้นเคยกับภาษาที่คล้ายกันและการย้ายพอร์ตที่ง่ายขึ้น
  3. C # ลบแหล่งที่เป็นไปได้ของข้อบกพร่องหรือรหัสที่เข้าใจผิดที่ได้รับการบันทึกไว้อย่างดีว่าเป็นสาเหตุของปัญหาในช่วงสี่ทศวรรษที่ผ่านมา
  4. C # ทำให้แนวปฏิบัติที่ดีที่สุดที่มีอยู่กับ C (การตกลงผ่านเอกสาร) บังคับใช้โดยคอมไพเลอร์
  5. C # ทำให้กรณีที่ผิดปกติเป็นกรณีที่มีรหัสชัดเจนมากขึ้นและเป็นกรณีปกติที่มีรหัสหนึ่งเขียนโดยอัตโนมัติ
  6. C # ใช้วิธีการพื้นฐานเดียวกันgotoสำหรับการกดปุ่มบล็อกเดียวกันจากcaseฉลากที่แตกต่างกันตามที่ใช้ใน C. มันแค่วางมันลงในกรณีอื่น ๆ
  7. C # ทำให้gotoแนวทางที่ใช้สะดวกและชัดเจนกว่าใน C โดยอนุญาตให้caseข้อความทำหน้าที่เป็นป้ายกำกับ

สรุปการออกแบบที่สมเหตุสมผล


* บางรูปแบบของ BASIC จะยอมให้ใครทำสิ่งที่ชอบGOTO (x AND 7) * 50 + 240ในขณะที่เปราะและด้วยเหตุนี้จึงเป็นกรณีที่โน้มน้าวใจเป็นพิเศษสำหรับการห้ามgotoใช้เพื่อแสดงภาษาที่สูงกว่าเทียบเท่ากับวิธีที่รหัสระดับล่างสามารถกระโดดได้ เลขคณิตตามค่าซึ่งมีความเหมาะสมมากกว่าเมื่อผลลัพธ์ของการรวบรวมมากกว่าสิ่งที่ต้องได้รับการปรับปรุงด้วยตนเอง การใช้งานอุปกรณ์ของ Duff โดยเฉพาะอย่างยิ่งให้ยืมตัวเองดีกับรหัสเครื่องที่เทียบเท่าหรือ IL เพราะแต่ละบล็อกของคำแนะนำมักจะมีความยาวเท่ากันโดยไม่จำเป็นต้องเพิ่มnopตัวเติม

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


17

คุณสามารถ 'ป้ายกำกับกรณีไปที่' http://www.blackwasp.co.uk/CSharpGoto.aspx

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


8

พวกเขาออกพฤติกรรมนี้โดยการออกแบบเพื่อหลีกเลี่ยงเมื่อไม่ได้ใช้โดยจะ แต่ทำให้เกิดปัญหา

มันสามารถใช้เฉพาะในกรณีที่ไม่มีคำสั่งในส่วนกรณีเช่น:

switch (whatever)
{
    case 1:
    case 2:
    case 3: boo; break;
}

4

พวกเขาเปลี่ยนคำสั่งเปลี่ยน (จาก C / Java / C ++) พฤติกรรมสำหรับ c # ฉันคิดว่าเหตุผลก็คือผู้คนลืมเกี่ยวกับการล้มและเกิดข้อผิดพลาด มีหนังสือเล่มหนึ่งที่ฉันอ่านว่าให้ใช้ goto เพื่อจำลองสถานการณ์ แต่มันฟังดูไม่ดีสำหรับฉัน


2
C # รองรับ goto แต่ไม่เข้าใจผิดเลยเหรอ? ว้าว. และไม่ใช่แค่นั้น C # เป็นภาษาเดียวที่ฉันรู้ว่ามันทำงานแบบนี้
Matthew Scharley

ฉันไม่ชอบมันในตอนแรก แต่ "fall-thru" เป็นสูตรสำหรับความหายนะ (โดยเฉพาะในหมู่โปรแกรมเมอร์จูเนียร์) ตามที่หลายคนชี้ให้เห็น C # ยังคงอนุญาตให้มีการตกหล่นสำหรับบรรทัดว่าง (ซึ่งส่วนใหญ่ กรณี) "Kenny" โพสต์ลิงก์ที่เน้นการใช้งานที่หรูหราไปกับสวิตช์ตัวพิมพ์ใหญ่
Pretzel

มันไม่ใช่เรื่องใหญ่ในความคิดของฉัน 99% ของเวลาที่ฉันไม่ต้องการให้ล้มและฉันถูกเผาโดยแมลงในอดีต
Ken

1
"สิ่งนี้ดูเหมือนจะไม่ใช่คำตอบที่ดีสำหรับฉัน" - ขอโทษที่ได้ยินเรื่องนี้เพราะคุณgoto caseเป็นคน ข้อได้เปรียบเหนือข้อผิดพลาดคือมันชัดเจน คนบางคนในที่นี่คัดค้านเพื่อgoto caseแสดงให้เห็นว่าพวกเขาได้รับการปลูกฝังเรื่อง "โกโตะ" โดยไม่มีความเข้าใจในปัญหาและไม่สามารถคิดด้วยตนเองได้ เมื่อ Dijkstra เขียนว่า "GOTO Considered Harmful" เขาพูดกับภาษาที่ไม่มีวิธีการอื่นในการเปลี่ยนลำดับการควบคุม
Jim Balter

@JimBalter แล้วว่าหลาย ๆ คนที่พูดบน Dijkstra ที่จะพูดนูว่า "การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากของความชั่วร้ายทั้งหมด" แม้ว่าจะอ้างว่าเป็นตอนที่นูได้รับอย่างชัดเจนเขียนเกี่ยวกับวิธีที่มีประโยชน์gotoสามารถเพิ่มประสิทธิภาพรหัส?
Jon Hanna

1

คุณสามารถประสบความสำเร็จเช่น c ++ โดยคำหลัก goto

EX:

switch(num)
{
   case 1:
      goto case 3;
   case 2:
      goto case 3;
   case 3:
      //do something
      break;
   case 4:
      //do something else
      break;
   case default:
      break;
}

9
ถ้ามีคนโพสต์เมื่อสองปีก่อน!

0

จำเป็นต้องใช้คำสั่งการข้ามเช่นการแบ่งหลังจากแต่ละบล็อกเคสรวมถึงบล็อกสุดท้ายไม่ว่าจะเป็นคำสั่งเคสหรือคำสั่งเริ่มต้น ด้วยข้อยกเว้นหนึ่งข้อ (ซึ่งแตกต่างจากคำสั่งเปลี่ยนของ C ++) C # ไม่สนับสนุนการตกหล่นจากป้ายกรณีหนึ่งไปยังอีกกรณีหนึ่ง ข้อยกเว้นหนึ่งคือถ้าคำสั่ง case ไม่มีรหัส

- เอกสารC # สวิตช์ ()


1
ฉันรู้ว่าพฤติกรรมนี้ได้รับการบันทึกไว้ฉันต้องการทราบว่าทำไมจึงเป็นอย่างนั้นและเป็นทางเลือกใด ๆ ที่จะได้รับพฤติกรรมแบบเดิม
Matthew Scharley

0

หลังจากแต่ละคำสั่ง case ต้องการคำสั่งbreakหรือgotoแม้ว่ามันจะเป็นตัวพิมพ์ใหญ่ก็ตาม


2
ถ้ามีคนโพสต์เมื่อสองปีก่อน!

1
@Poldie มันเป็นเรื่องตลกในครั้งแรก ... Shilpa คุณไม่จำเป็นต้องหยุดพักหรือกลับไปทุกกรณีเพียงแค่ทุกกรณีที่มีรหัสเป็นของตัวเอง คุณสามารถมีหลายกรณีที่ใช้รหัสร่วมกันได้
ไม่ฝักใฝ่ฝ่ายใด

0

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


0

switch (C # Reference) พูดว่า

C # ต้องการจุดสิ้นสุดของส่วนสวิตช์รวมถึงส่วนสุดท้าย

ดังนั้นคุณต้องเพิ่มส่วนbreak;ของคุณdefaultมิฉะนั้นจะยังคงมีข้อผิดพลาดของคอมไพเลอร์


ขอบคุณสิ่งนี้ช่วยฉัน;)
CareTaker22

-1

C # ไม่สนับสนุนคำสั่ง switch / case ไม่แน่ใจว่าทำไม แต่ไม่มีการสนับสนุนจริงๆ การเชื่อมต่อ


นี่มันผิดมาก ดูคำตอบอื่น ๆ ... ผลของนัย fallthrough สามารถรับได้กับอย่างชัดเจน goto caseนี่เป็นตัวเลือกการออกแบบโดยเจตนาโดยนักออกแบบ C #
Jim Balter

-11

คุณลืมเพิ่ม "break;" คำสั่งลงในกรณีที่ 3 ในกรณีที่ 2 คุณเขียนลงในบล็อก if ดังนั้นลองทำสิ่งนี้:

case 3:            
{
    ans += string.Format("{0} hundred and ", numbers[number / 100]);
    break;
}


case 2:            
{
    int t = (number / 10) % 10;            
    if (t == 1)            
    {                
        ans += teens[number % 10];                
    }            
    else if (t > 1)                
    {
        ans += string.Format("{0}-", tens[t]);        
    }
    break;
}

case 1:            
{
    int o = number % 10;            
    ans += numbers[o];            
    break;        
}

default:            
{
    throw new ArgumentException("number");
}

2
สิ่งนี้สร้างผลลัพธ์ที่ผิดอย่างมหาศาล ฉันออกจากสวิตช์งบออกโดยการออกแบบ คำถามคือสาเหตุที่คอมไพเลอร์ C # เห็นว่านี่เป็นข้อผิดพลาดเมื่อแทบไม่มีภาษาอื่นที่มีข้อ จำกัด นี้
Matthew Scharley

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