Ganti pernyataan gagal ... haruskah diizinkan? [Tutup]

120

Selama yang saya ingat, saya telah menghindari penggunaan fall-through pernyataan switch. Sebenarnya, saya tidak dapat mengingatnya pernah memasuki kesadaran saya sebagai cara yang mungkin untuk melakukan sesuatu karena sudah dibor di kepala saya sejak awal bahwa itu tidak lebih dari bug dalam pernyataan switch. Namun, hari ini saya menemukan beberapa kode yang menggunakannya dengan desain, yang membuat saya langsung bertanya-tanya apa yang dipikirkan semua orang di komunitas tentang fall-through pernyataan switch.

Apakah itu sesuatu yang seharusnya tidak diizinkan secara eksplisit oleh bahasa pemrograman (seperti C #, meskipun ia menyediakan solusi) atau apakah itu fitur dari bahasa apa pun yang cukup kuat untuk ditinggalkan di tangan programmer?

Sunting: Saya tidak cukup spesifik untuk apa yang saya maksud dengan fall-through. Saya sering menggunakan tipe ini:

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

Namun, saya khawatir tentang hal seperti ini.

   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;
   }

Dengan cara ini setiap kali case adalah 0, 1 itu akan melakukan semua yang ada di pernyataan switch. Saya telah melihat ini dengan sengaja dan saya tidak tahu apakah saya setuju bahwa pernyataan sakelar harus digunakan dengan cara ini. Saya rasa contoh kode pertama ini sangat berguna dan aman. Yang kedua sepertinya berbahaya.

Fostah
sumber
51
Tidak setuju tentang tidak konstruktif. 27 suara positif pada pertanyaan mengatakan ada orang lain yang merasa seperti ini juga.
Wes Modes
2
"tidak konstruktif" adalah alasan yang lama dan agak kabur. Saat ini, pertanyaan semacam itu dapat, dan harus, ditandai untuk ditutup sebagai murni berdasarkan opini. SO adalah untuk pertanyaan yang dapat dijawab tentang kode atau desain, bukan 'apa pendapat "komunitas" [apa pun itu] tentang pendapat saya tentang Fitur X'. Programmer.SE akan lebih cocok, tetapi bahkan kemudian, nilai pertanyaan yang begitu luas dan berdasarkan opini masih sangat meragukan.
underscore_d
1
Ada kasus penggunaan yang sangat jelas di mana fallthrough digunakan, jadi tidak yakin mengapa itu didasarkan pada opini. Saya setuju pertanyaan ini agak tidak jelas, tetapi seharusnya tidak ditutup karena tidak konstruktif. Mungkin hanya diedit untuk menjadi kasus yang lebih jelas: "Kapan fallthrough digunakan?" Yang punya jawaban jelas. Teks C ++ yang bagus membahas hal ini, membingungkan untuk pembuat kode baru (atau bahkan pembuat kode yang lebih berpengalaman dari bahasa lain), jadi sepertinya pertanyaan yang bagus untuk SO. Memang, sepertinya pertanyaan SO yang secara prototipe bagus. Meskipun cara menanyakannya tidak sempurna.
eric

Jawaban:

89

Itu mungkin tergantung pada apa yang Anda anggap salah. Saya setuju dengan hal semacam ini:

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;
}

Tetapi jika Anda memiliki label kasus diikuti dengan kode yang jatuh ke label kasus lain, saya akan selalu menganggap itu jahat. Mungkin memindahkan kode umum ke suatu fungsi dan memanggil dari kedua tempat akan menjadi ide yang lebih baik.

Dan perlu diketahui bahwa saya menggunakan definisi FAQ C ++ tentang "jahat"

Fred Larson
sumber
Saya setuju dengan Anda: Saya tidak menganggap contoh Anda sebagai fallthrough, dan jika ada kasus di mana saya ingin mengeksekusi banyak blok kode melalui fallthrough, mungkin ada cara yang lebih baik untuk melakukannya.
Dave DuPlantis
10
1 hanya untuk definisi "jahat". Saya akan meminjam itu, terima kasih ;-)
Joachim Sauer
Saya mengerti maksud Anda dan Anda benar, tetapi teladan Anda sangat buruk. Tidak ada yang boleh menguji angka uji seperti ini. Scnr, bagaimanapun juga jawaban Anda benar.
Tim Büthe
Contoh kasus, itulah hal yang tidak diizinkan oleh C #. Kemungkinan karena suatu alasan :-)
Joey
Sayangnya definisi kejahatan tampaknya telah offline
Semoga
57

Itu pedang bermata dua. Terkadang sangat berguna, tetapi seringkali berbahaya.

Kapan itu enak? Bila Anda ingin 10 kasus semua diproses dengan cara yang sama ...

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;
}

Satu aturan yang saya suka adalah jika Anda pernah melakukan sesuatu yang mewah di mana Anda mengecualikan jeda, Anda memerlukan komentar yang jelas / * FALLTHROUGH * / untuk menunjukkan bahwa itu adalah niat Anda.

John M
sumber
3
+1! Saya setuju bahwa terkadang ini adalah jawaban yang benar, dan saya DOUBLY setuju bahwa jika memang sesuai desain, itu harus dikomentari. (Dalam review kode, saya akan MEMBUAT pengembang lain mengomentari fall-through bukan kosong yang disain. Bukan itu yang terjadi; kami adalah toko C # di mana itu tidak didukung :)
John Rudy
4
Saya sendiri menggunakan / * FALL THROUGH * / komentar di kode saya jika sesuai. =]
strager
2
Jika Anda PC-Lint, Anda harus memasukkan / * - fallthrough * /, jika tidak maka akan mengeluh.
Steve Melnikoff
1
Namun, jika Anda ingin memperjelas bahwa perilakunya disengaja, sebaiknya Anda juga if(condition > 1 && condition <10) {condition = 1}; switch(...menyelamatkan kasus (agar terlihat jelas) dan semua orang dapat melihat bahwa Anda benar-benar ingin kasus tersebut memicu tindakan yang sama.
Tandai
1
Ini masih pengkodean yang buruk. Kasus pertama harus memanggil fungsi, kasus kedua harus memanggil fungsi yang sama kemudian memanggil fungsi kedua.
BlueRaja - Danny Pflughoeft
21

Pernahkah Anda mendengar tentang perangkat Duff ? Ini adalah contoh yang bagus dalam menggunakan fallthrough sakelar.

Ini adalah fitur yang dapat digunakan dan dapat disalahgunakan, seperti hampir semua fitur bahasa.

tzot.dll
sumber
Pelajari sesuatu yang baru setiap hari - terima kasih! Saya merasa untuk devs miskin yang melalui perubahan seperti itu.
Justin R.
Wow, itu cukup sesuatu untuk membungkus otak Anda.
Fostah
6
Perhatikan komentar Duff yang dikutip dalam artikel itu: "Kode ini membentuk semacam argumen dalam debat itu, tapi saya tidak yakin apakah itu mendukung atau menentang."
John Carter
Perangkat Duff jelas jahat
Marjeta
21

Fall-through adalah hal yang berguna, tergantung pada apa yang Anda lakukan. Pertimbangkan cara yang rapi dan mudah dipahami ini untuk mengatur opsi:

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

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

Bayangkan melakukan ini dengan if / else. Ini akan menjadi berantakan.

Lucas Oman
sumber
1
Saya menemukan jenis fallthrough ini sangat membantu
Penjual Mitchel
jika 'melakukan sesuatu' lebih dari sekedar sedikit daripada sakelar akan lebih berantakan daripada if / else. Dengan if / else jelas dimana variabel yang diuji. Bahkan contoh kecil ini tidak akan terlihat terlalu buruk dengan if / else di mata saya
grenix
10

Ini bisa sangat berguna beberapa kali, tetapi secara umum, tidak ada kesalahan dalam perilaku yang diinginkan. Pergantian harus diizinkan, tetapi tidak implisit.

Contoh, untuk memperbarui versi lama beberapa data:

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

Saya ingin sintaks yang berbeda untuk fallback di sakelar, sesuatu seperti, errr ..

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

Catatan: Ini sudah bisa dilakukan dengan enum, jika Anda mendeklarasikan semua kasus di enum Anda menggunakan flag, bukan? Kedengarannya juga tidak terlalu buruk; kasus bisa (harus?) sangat baik menjadi bagian dari enum Anda sudah.

Mungkin ini akan menjadi kasus yang bagus (tidak ada permainan kata-kata) untuk antarmuka yang lancar menggunakan metode ekstensi? Sesuatu seperti, errr ...

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

Meskipun itu mungkin bahkan kurang terbaca: P

Erik van Brakel
sumber
1
Saya sangat menyukai saran pertama. Itu terbaca dengan sangat baik dan menghemat beberapa baris kode. Yang kedua membutuhkan waktu satu menit untuk membungkus otak saya, tetapi masuk akal, tetapi tampaknya siap untuk menjadi berantakan.
Fostah
1
Ya, itulah yang saya pikirkan juga, itu hanya pikiran kentut acak yang saya letakkan, dalam upaya menemukan cara berbeda untuk beralih menggunakan konstruksi bahasa yang ada.
Erik van Brakel
Berlari melalui pertanyaan ini dan saya benar-benar menyukai ini! Punya ide tentang cara meningkatkan keterbacaan ini tetapi SO tidak mengizinkan saya memposting komentar multi-baris :( Seseorang harus menerapkan ini seperti POC, terlihat menyenangkan untuk diterapkan dan digunakan (:
Victor Elias
5

Seperti apa pun: jika digunakan dengan hati-hati, ini bisa menjadi alat yang elegan.

Namun, menurut saya kekurangannya lebih dari sekadar membenarkan untuk tidak menggunakannya, dan akhirnya tidak mengizinkannya lagi (C #). Diantara masalahnya adalah:

  • mudah untuk "melupakan" istirahat
  • tidak selalu jelas bagi pengelola kode bahwa pemutusan yang dihilangkan itu disengaja

Penggunaan yang baik dari switch / case fall-through:

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

Baaaaad penggunaan switch / case fall-through:

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

Ini dapat ditulis ulang menggunakan if / else konstruksi tanpa kerugian sama sekali menurut saya.

Kata terakhir saya: jauhi label case fall-through seperti pada contoh buruk , kecuali Anda mempertahankan kode warisan di mana gaya ini digunakan dan dipahami dengan baik.

steffenj
sumber
3
Anda bisa menulis ulang sebagian besar konstruksi bahasa menggunakan if () dan goto ... yang tidak membenarkan pengabaian konstruksi eksplisit.
Shog9
2
Saya tahu tetapi konstruksi switch / case yang secara eksplisit menggunakan "case 1: some code case 2: some more code case 3: final code break;" dapat dan harus ditulis ulang menggunakan if / else if (pendapat saya).
steffenj
1
Perhatikan bahwa sakelar bisa berkali-kali lebih cepat daripada daftar if-elses. Switch menggunakan tabel lompatan, atau jika tidak memungkinkan, lompatan bersyaratnya akan disusun dalam pohon keputusan, bukan dalam daftar linier lompatan bersyarat. Pernyataan If-else yang terstruktur dengan baik akan, paling banter, sama cepatnya.
Jochem Kuijpers
4

Itu sangat kuat dan berbahaya. Masalah terbesar dengan fall-through adalah bahwa hal itu tidak eksplisit. Misalnya, jika Anda menemukan kode yang sering diedit yang memiliki sakelar dengan fall-through, bagaimana Anda tahu itu disengaja dan bukan bug?

Di mana pun saya menggunakannya, saya memastikan komentar itu benar:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }
Dan Hulton
sumber
2
Maksudnya jelas jika tidak ada kode untuk kasus 'pertama'. Jika Anda membuat kode di sana, dan juga ingin menjalankan kode untuk kasus 'kedua', maka komentar itu penting. Tapi saya akan menghindari skenario itu.
Joel Coehoorn
1
@ Joel - Saya setuju sepenuhnya. Di toko saya, ada orang yang memberikan komentar yang salah dalam kasus seperti itu, dan saya pikir itu mengurangi keterbacaan. Jika ada kode di sana, masukkan komentar fallthrough.
Fred Larson
Apa yang kami maksud "tidak eksplisit". Inilah mengapa kita putus; Bahasa lain melakukan ini. Ini hanya masalah bagi orang-orang yang belum mempelajari bahasa mereka secara berurutan. C # menuntutnya untuk kasus default ffs, tanpa alasan untuk imho.
mckenzm
3

Menggunakan fall-through seperti pada contoh pertama Anda jelas OK, dan saya tidak akan menganggapnya sebagai fall-through yang nyata.

Contoh kedua berbahaya dan (jika tidak dikomentari secara ekstensif) tidak jelas. Saya mengajar siswa saya untuk tidak menggunakan konstruksi seperti itu kecuali mereka menganggap itu sepadan dengan upaya untuk mencurahkan blok komentar padanya, yang menjelaskan bahwa ini adalah kesalahan yang disengaja, dan mengapa solusi ini lebih baik daripada alternatifnya. Ini mencegah penggunaan yang ceroboh, tetapi tetap membuatnya diperbolehkan dalam kasus di mana digunakan untuk keuntungan.

Ini kurang lebih sama dengan apa yang kami lakukan dalam proyek luar angkasa ketika seseorang ingin melanggar standar pengkodean: mereka harus mengajukan dispensasi (dan saya dipanggil untuk memberi nasihat tentang keputusan itu).

Wouter van Ooijen
sumber
2

Saya tidak suka switchpernyataan saya gagal - terlalu rawan kesalahan dan sulit dibaca. Satu-satunya pengecualian adalah ketika beberapa casepernyataan semuanya melakukan hal yang persis sama.

Jika ada beberapa kode umum yang ingin digunakan oleh beberapa cabang pernyataan switch, saya mengekstraknya menjadi fungsi umum terpisah yang dapat dipanggil di cabang mana pun.

Matt Dillard
sumber
1

Dalam beberapa kasus, menggunakan fall-through adalah tindakan kemalasan di pihak programmer - mereka bisa menggunakan rangkaian || pernyataan, misalnya, tetapi menggunakan serangkaian kasus sakelar 'penampung-semua'.

Karena itu, saya merasa mereka sangat membantu ketika saya mengetahuinya pada akhirnya saya akan tetap memerlukan opsi (misalnya dalam respons menu), tetapi belum menerapkan semua pilihan. Demikian juga, jika Anda melakukan fall-through untuk 'a' dan 'A', saya merasa jauh lebih bersih menggunakan sakelar fall-through daripada pernyataan gabungan if.

Ini mungkin masalah gaya dan bagaimana pemrogram berpikir, tetapi saya umumnya tidak suka menghapus komponen bahasa atas nama 'keamanan' - itulah sebabnya saya cenderung ke arah C dan varian / turunannya lebih dari, katakanlah, Jawa. Saya suka bisa bermain-main dengan petunjuk dan sejenisnya, bahkan ketika saya tidak punya "alasan" untuk itu.

warren
sumber
Sesungguhnya. Pernyataan sakelar melompat melalui nilai yang ditetapkan ke sekelompok lokasi kode tertentu. Sementara kompiler mungkin akan menangkap hal-hal seperti itu, ada nilai kecepatan dan ketepatan untuk pernyataan switch atas serangkaian atau pernyataan. Jika p adalah 0 atau p adalah 1 atau p adalah 2. Saya sebenarnya harus mengevaluasi apakah p adalah 0 dan p adalah 1, daripada melompat ke lokasi p = 2 dalam kode. Yang kebetulan identik dengan p0, dan p1. Tapi, saya tidak pernah memeriksanya.
Tatarize
1

Fall-through harus digunakan hanya jika digunakan sebagai tabel lompat ke dalam blok kode. Jika ada bagian dari kode dengan jeda tanpa syarat sebelum lebih banyak kasus, semua grup kasus harus diakhiri dengan cara itu.

Yang lainnya adalah "jahat".

BCS
sumber