มีใครยังใช้ [goto] ใน C # และถ้าเป็นเช่นนั้นทำไม? [ปิด]


104

ฉันสงสัยว่ามีใครยังใช้ไวยากรณ์คำหลัก "goto" ใน C # หรือไม่และมีเหตุผลอะไรที่เป็นไปได้ในการทำเช่นนั้น

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

ไปที่คำจำกัดความของคำหลัก


3
คุณหมายถึงอะไร "ยัง"? มีช่วงเวลาหนึ่งที่ผู้คนใช้มันตลอดเวลา [ใน c #] หรือไม่?
Massif

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

50
หากผู้อ่าน "กระโดดไปรอบ ๆ " รหัสเป็นการปฏิบัติที่ไม่ดีคุณจะหลีกเลี่ยง "หยุด" "ต่อ" "โยน" และ "ย้อนกลับ" ด้วยหรือไม่? ทั้งหมดนี้ทำให้เกิดสาขาในขั้นตอนการควบคุมบางครั้งก็เป็นสาขาที่ไม่ใช่สาขา "โยน" ไม่ได้บอกคุณด้วยซ้ำว่ามันกำลังจะไปที่ไหน
Eric Lippert

2
ฉันใช้gotoเพื่อทำลายลูปและกลับไปที่คำสั่งเริ่มต้นตามเงื่อนไขที่เฉพาะเจาะจง
Nitin Sawant

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

คำตอบ:


93

มีบางกรณี (หายาก) ที่ goto สามารถปรับปรุงการอ่านได้จริง ในความเป็นจริงเอกสารที่คุณเชื่อมโยงกับรายการสองตัวอย่าง:

การใช้ goto โดยทั่วไปคือการถ่ายโอนการควบคุมไปยังฉลากสวิตช์เคสเฉพาะหรือเลเบลเริ่มต้นในคำสั่งสวิตช์

คำสั่ง goto ยังมีประโยชน์ในการออกจากลูปที่ซ้อนกันลึก ๆ

นี่คือตัวอย่างสำหรับอันหลัง:

for (...) {
    for (...) {
        ...
        if (something)
            goto end_of_loop;
    }
}

end_of_loop:

แน่นอนว่ามีวิธีอื่น ๆ ในการแก้ปัญหานี้เช่นกันเช่นการปรับโครงสร้างโค้ดใหม่ให้เป็นฟังก์ชันการใช้บล็อกหลอกรอบ ๆ เป็นต้น (ดูรายละเอียดในคำถามนี้ ) ตามหมายเหตุด้านข้างนักออกแบบภาษา Java ตัดสินใจที่จะแบนgoto โดยสิ้นเชิงและแนะนำคำสั่งแบ่งที่มีป้ายกำกับแทน


48
โดยปกติฉันจะพยายาม refactor สิ่งนี้เพื่อใส่ลูปในวิธีการแยกต่างหากซึ่งฉันสามารถกลับมาจาก ...
Jon Skeet

2
@Heinzi - ฉันไม่เคยเห็น goto ได้รับการรับประกัน เช่นเดียวกับที่จอนกล่าวว่าหากมีการ "รับประกัน" รหัสจะขอให้มีการปรับโครงสร้างใหม่
manojlds

29
มีป้ายกำกับว่า break เป็นวิธีที่ยาวกว่าในการพูดว่า goto เพราะมันทำสิ่งที่แปลกประหลาดเหมือนกัน ....
Jesus Ramos

20
@ พระเยซู แต่แล้วด้วย goto คุณสามารถไปได้ทุกที่ ป้ายกำกับช่วยให้คุณออกนอกวงได้
mihsathe

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

66

ส่วนนี้ผมจำได้

switch (a)     
{ 
    case 3: 
        b = 7;
        // We want to drop through into case 4, but C# doesn't let us
    case 4: 
        c = 3;
        break; 
    default: 
        b = 2;
        c = 4;
        break; 
}

อะไรทำนองนี้

switch (a)     
{
    case 3: 
        b = 7;
        goto case 4;    
    case 4: 
        c = 3;
        break;     
    default: 
        b = 2;
        c = 4;
        break;
}

อ้างถึงสิ่งนี้


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

11
@Brian Scott, V4Vendetta. เว้นแต่ฉันจะเข้าใจผิดว่าคำสั่งแรกไม่ได้รวบรวมใน C # นั่นจะช่วยให้โปรแกรมเมอร์เข้าใจ
Jodrell

2
V4Vendetta เหตุใดคุณจึงแทรกตัวแบ่งข้อมูลโค้ดแรก ... เป็นการดีกว่าที่จะแสดงโดยไม่หยุดพักมิฉะนั้นตัวอย่างข้อมูลทั้งสองจะทำสิ่งที่แตกต่างกัน สาเหตุที่คุณต้องการ goto ในตัวอย่างที่สองนั้นเป็นเพราะอันแรกไม่ได้รวบรวมใน C # (เหมือนใน C)
Stephen Holt

23

ฉันใช้มันอย่างกว้างขวางในEduasyncเพื่อแสดงชนิดของรหัสที่คอมไพเลอร์สร้างให้คุณเมื่อใช้วิธีการ async ใน C # 5 คุณจะเห็นสิ่งเดียวกันในบล็อกตัววนซ้ำ

ในรหัส "ปกติ" ฉันจำครั้งสุดท้ายที่ใช้ไม่ได้ ...


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

1
@ ไบรอัน: มันไม่ชัดเจนจริงๆว่าคุณหมายถึงอะไร Eduasync แสดงรหัส C # ที่เทียบเท่ากับสิ่งที่คอมไพเลอร์ทำเพื่อคุณ - และสร้างรหัสที่ใช้ goto ได้อย่างมีประสิทธิภาพ ...
Jon Skeet

9

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


7
แน่นอนว่า "หยุดพัก" / "ดำเนินการต่อ" เป็นแนวทางที่ดีกว่าในการจัดการแบบวนซ้ำแทนที่จะต้องให้ตัวแก้ไขโค้ดข้ามไปรอบ ๆ ซอร์สโค้ดพยายามที่จะเข้าใจว่าขั้นตอนต่อไปเกิดขึ้นที่ไหน?
Brian Scott

6
ไม่ใช่ถ้าคุณมีลูปซ้อนกัน
Jesus Ramos

1
โอเคฉันเห็นว่านี่เป็นสถานการณ์ที่ถูกต้อง
Brian Scott

1
ในสภาวะผิดพลาดคุณควรพิจารณาโยนข้อยกเว้น
Jodrell

6
หากคุณต้องการจัดการข้อผิดพลาดภายในโดยไม่มีข้อยกเว้นนี่เป็นวิธีที่ถูกต้องในการดำเนินการดังกล่าว
Jesus Ramos

8

gotoผมจำไม่ได้เคยใช้ แต่บางทีมันอาจช่วยเพิ่มความตั้งใจของการวนซ้ำตลอดไปที่คุณไม่ต้องการออกจริงๆ (ไม่breakแต่คุณยังทำได้returnหรือthrow):

forever: {
  // ...
  goto forever;
}

จากนั้นอีกครั้งง่ายๆwhile (true)ควรพอเพียง ...

นอกจากนี้คุณยังสามารถใช้ในสถานการณ์ที่คุณต้องการซ้ำแรกของวงที่จะเริ่มต้นในช่วงกลางของวง: ดูที่นี่เช่น


คำตอบที่เชื่อมโยงมีการใช้งานที่ "น่าสนใจ" ของgoto.. และwhile(true) {..}ไม่ได้เป็นการใช้งานที่น่าสนใจ ..
user2864740

5

คอมไพเลอร์ใช้gotoคำสั่งในส่วนต่างๆของโค้ดที่สร้างขึ้นตัวอย่างเช่นในประเภทบล็อกตัววนซ้ำที่สร้างขึ้น (สร้างขึ้นเมื่อใช้yield returnคีย์เวิร์ด - ฉันค่อนข้างแน่ใจว่าประเภทการจัดลำดับ XML ที่สร้างขึ้นก็มีgotoคำสั่งบางส่วนอยู่ในนั้นเช่นกัน

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

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

ดูคำสั่ง Go-to ที่ถือว่าเป็นอันตรายสำหรับการโต้แย้งgotoเช่นเดียวกับประวัติศาสตร์การเขียนโปรแกรมแบบคลาสสิก


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

@Lohoris ฉันไม่ได้ซื้อมัน - ตัวอย่างทุกที่ผมเคยเห็นที่ข้ามไป "ช่วยเพิ่มความสามารถในการอ่าน" (รวมถึงคำตอบที่นี่) จะไกลอ่านได้มากขึ้นหลังจากที่บาง refactoring ง่าย
จัสติน

3
@ จัสตินไม่บางครั้งการวนซ้ำที่ซ้อนกันเป็นเพียงวิธีที่เป็นธรรมชาติที่สุดในการทำบางสิ่งบางอย่างเช่นหากคุณกำลังสำรวจอาร์เรย์อาร์เรย์
o0 '

6
@ จัสตินมันไม่ชัดเจนเสมอไปที่จะให้มันเป็นฟังก์ชัน คุณกำลังบังคับบางสิ่งบางอย่าง (มีหน้าที่) เพียงเพื่อหลีกเลี่ยงสิ่งที่คุณเกลียดตามหลักศาสนา (โดยใช้ goto) บ่งบอกชัดเจนว่าทำอะไรผิด
o0 '

3
@Justin การเรียกใช้ฟังก์ชันมีค่าใช้จ่าย gotoไม่. สิ่งที่ต้องพิจารณา
Dan Bechard

2

โปรเซสเซอร์ใช้คำสั่งกระโดดอย่างน้อยหนึ่งคำสั่งและฉันแน่ใจว่าข้อความจำนวนมากใช้คำสั่งเหล่านี้ในการนำไปใช้หรือตีความ

สิ่งที่ดีอย่างหนึ่งเกี่ยวกับการใช้ภาษารุ่นที่ 3หรือ4ก็คือรายละเอียดทางกายภาพเหล่านี้จะแยกออกไปจากเรา ในขณะที่เราควรคำนึงถึงกฎแห่งนามธรรมที่รั่วไหลฉันคิดว่าเราควรใช้เครื่องมือของเราตามที่ตั้งใจไว้ ( ขออภัย ) ถ้าฉันกำลังเขียนโค้ดและgotoดูเหมือนจะเป็นความคิดที่ดีก็คงถึงเวลาที่ต้อง refactor จุดประสงค์ของภาษาที่มีโครงสร้างคือเพื่อหลีกเลี่ยง "การกระโดด" เหล่านี้และเพื่อสร้างกระแสเชิงตรรกะในวิศวกรรมของเรา

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

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

ฉันหวังว่าฉันจะไม่ผิดที่รีบวิ่งไปที่ " โรงเก็บจักรยาน " ที่นี่ อย่างที่ Kragen พูดอะไรที่ดีพอสำหรับDijkstraคือสิ่งที่ดีพอสำหรับฉัน


1
การถ่ายdynamicวัตถุแล้วเดินเป็นกราฟวัตถุที่มีพจนานุกรมหลายเล่มเพื่อลงลึกถึงค่าที่ฉันต้องการ ไม่สมเหตุสมผลที่จะใช้วิธีการที่มีพารามิเตอร์dynamicแต่คาดว่าจะมีรูปร่างของวัตถุที่แน่นอน ด้วย goto เพื่อแยกชั้นหลาย ๆ ชั้นและเดินต่อไปตามคอลเล็กชันของวัตถุเหล่านี้ [ฉันไม่ได้เป็นเจ้าของประเภทดังนั้นฉันจึงไม่สามารถให้การเข้าถึงที่ดีกว่านี้ได้ดังนั้นการสะท้อนหรือไดนามิกจึงเป็นแบบนั้น]
Chris Marisic

-6

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


10
คุณต้องการแยกออกจากลูปหากไม่มีประเด็นที่จะรักษาการดำเนินการของลูป มิฉะนั้นคุณจะเสียเวลาในการประมวลผลมากขึ้นในการวนซ้ำอีกต่อไปโดยไม่มีเหตุผล
Skuld

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