Beralih pernyataan fallthrough adalah salah satu alasan utama pribadi saya untuk cinta switch
vs if/else if
konstruksi. Contohnya ada di sini:
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;
}
Orang pintar merasa ngeri karena string[]
harus dinyatakan di luar fungsi: well, mereka, ini hanya sebuah contoh.
Kompiler gagal dengan kesalahan berikut:
Kontrol tidak dapat dialihkan dari satu label case ('case 3:') ke yang lain Kontrol tidak dapat dialihkan dari satu label case ('case 2:') ke yang lain
Mengapa? Dan adakah cara untuk mendapatkan perilaku semacam ini tanpa memiliki tiga if
s?
c#
switch-statement
Matthew Scharley
sumber
sumber
"Mengapa" adalah untuk menghindari kejatuhan yang tidak disengaja, yang untuk itu saya berterima kasih. Ini bukan sumber bug yang umum di C dan Java.
Solusinya adalah menggunakan goto, mis
Secara umum desain switch / casing agak disayangkan menurut saya. Ini terjebak terlalu dekat dengan C - ada beberapa perubahan berguna yang dapat dibuat dalam hal pelingkupan dll. Bisa dibilang sebuah saklar yang lebih cerdas yang dapat melakukan pencocokan pola dll akan sangat membantu, tapi itu benar-benar berubah dari sakelar menjadi "memeriksa urutan kondisi" - pada titik mana nama yang berbeda mungkin akan dipanggil.
sumber
if (result = true) { }
Switch fallthrough secara historis merupakan salah satu sumber utama bug dalam perangkat lunak modern. Perancang bahasa memutuskan untuk mewajibkan melompat di akhir kasing, kecuali jika Anda langsung ke kasing langsung tanpa proses.
sumber
case 1, 2:
dalam bahasa yang memungkinkan itu. Apa yang saya tidak akan pernah mengerti adalah mengapa bahasa modern tidak akan memilih untuk membiarkan itu terjadi.case 1, 2:
adalah label tunggal tetapi dengan beberapa nama. - FWIW, saya pikir sebagian besar bahasa yang melarang jatuh bukan kasus khusus "label kasus berurutan" indium melainkan memperlakukan label kasus sebagai anotasi pada pernyataan berikutnya dan memerlukan pernyataan terakhir sebelum pernyataan dilabeli dengan (satu atau lebih banyak) label case menjadi lompatan.Untuk menambah jawaban di sini, saya pikir ada baiknya mempertimbangkan pertanyaan yang berlawanan dalam hubungannya dengan ini, yaitu. mengapa C membiarkan jatuh-di tempat pertama?
Setiap bahasa pemrograman tentu saja melayani dua tujuan:
Karena itu, penciptaan bahasa pemrograman apa pun merupakan keseimbangan antara cara terbaik untuk melayani dua tujuan ini. Di satu sisi, semakin mudah untuk berubah menjadi instruksi komputer (apakah itu kode mesin, bytecode seperti IL, atau instruksi ditafsirkan pada eksekusi) maka lebih mampu bahwa proses kompilasi atau interpretasi akan menjadi efisien, dapat diandalkan dan kompak dalam keluaran. Diambil secara ekstrem, tujuan ini menghasilkan penulisan kami yang tepat di assembly, IL, atau bahkan op-code mentah, karena kompilasi termudah adalah di mana tidak ada kompilasi sama sekali.
Sebaliknya, semakin banyak bahasa yang mengekspresikan niat programmer, daripada sarana yang digunakan untuk tujuan itu, semakin dimengerti program baik saat menulis maupun selama pemeliharaan.
Sekarang,
switch
selalu bisa dikompilasi dengan mengubahnya menjadi rantaiif-else
blok yang setara atau serupa, tetapi ia dirancang sebagai memungkinkan kompilasi menjadi pola perakitan umum tertentu di mana seseorang mengambil nilai, menghitung offset dari itu (apakah dengan melihat sebuah tabel diindeks oleh hash sempurna dari nilai, atau dengan aritmatika aktual pada nilai *). Perlu dicatat pada titik ini bahwa hari ini, kompilasi C # kadang-kadang akan berubahswitch
menjadi setaraif-else
, dan kadang-kadang menggunakan pendekatan lompat berbasis hash (dan juga dengan C, C ++, dan bahasa lain dengan sintaks yang sebanding).Dalam hal ini ada dua alasan bagus untuk membiarkan jatuh:
Itu terjadi secara alami: jika Anda membangun tabel lompatan ke dalam satu set instruksi, dan salah satu dari batch instruksi sebelumnya tidak mengandung semacam lompatan atau pengembalian, maka eksekusi akan secara alami berkembang ke batch berikutnya. Mengizinkan fall-through adalah apa yang akan "terjadi begitu saja" jika Anda mengubah
switch
-menggunakan C-tabel-menggunakan kode mesin.Coder yang menulis dalam assembly sudah terbiasa dengan yang setara: ketika menulis tabel lompat dengan tangan di assembly, mereka harus mempertimbangkan apakah suatu blok kode tertentu akan berakhir dengan return, lompatan di luar tabel, atau hanya melanjutkan ke blok berikutnya. Karena itu, meminta pembuat kode untuk menambahkan secara eksplisit
break
jika diperlukan juga "alami" untuk pembuat kode.Oleh karena itu, pada saat itu, merupakan upaya yang masuk akal untuk menyeimbangkan dua tujuan bahasa komputer karena berkaitan dengan kode mesin yang diproduksi, dan ekspresi kode sumber.
Namun, empat dekade kemudian, segalanya tidak persis sama, karena beberapa alasan:
switch
menjadi berubahif-else
karena dianggap pendekatan yang paling efisien, atau berubah menjadi varian esoterik khusus dari pendekatan jump-table yang lebih tinggi. Pemetaan antara pendekatan tingkat tinggi dan rendah tidak sekuat dulu.switch
blok menggunakan fall-through selain beberapa label pada blok yang sama, dan dianggap bahwa penggunaan kasus di sini berarti bahwa 3% ini sebenarnya jauh lebih tinggi dari biasanya). Jadi bahasa yang dipelajari membuat yang tidak biasa lebih siap melayani daripada yang umum.Terkait dengan dua poin terakhir, pertimbangkan kutipan berikut dari edisi K&R saat ini:
Jadi, dari mulut kuda, jatuh dalam C adalah masalah. Ini dianggap praktik yang baik untuk selalu mendokumentasikan kegagalan dengan komentar, yang merupakan penerapan prinsip umum bahwa seseorang harus mendokumentasikan di mana seseorang melakukan sesuatu yang tidak biasa, karena itulah yang akan menyebabkan pemeriksaan kode kemudian dan / atau membuat kode Anda terlihat seperti itu. memiliki bug pemula di dalamnya padahal sebenarnya benar.
Dan ketika Anda memikirkannya, kode seperti ini:
Adalah menambahkan sesuatu untuk membuat fall-through eksplisit dalam kode, itu bukan sesuatu yang dapat dideteksi (atau yang ketidakhadirannya dapat dideteksi) oleh kompiler.
Dengan demikian, fakta bahwa on harus eksplisit dengan fall-through dalam C # tidak menambah hukuman bagi orang yang menulis dengan baik dalam bahasa gaya C lainnya, karena mereka sudah akan eksplisit dalam fall-throughs mereka. †
Akhirnya, penggunaan di
goto
sini sudah menjadi norma dari C dan bahasa lain seperti:Dalam kasus seperti ini di mana kita ingin sebuah blok dimasukkan dalam kode yang dieksekusi untuk nilai selain hanya yang membawa satu ke blok sebelumnya, maka kita sudah harus menggunakan
goto
. (Tentu saja, ada cara dan cara untuk menghindarinya dengan persyaratan yang berbeda tetapi itu berlaku untuk hampir semua yang berkaitan dengan pertanyaan ini). Karena itu, C # dibangun dengan cara yang sudah biasa untuk menangani satu situasi di mana kami ingin menekan lebih dari satu blok kode dalam aswitch
, dan hanya menggeneralisasikannya untuk mencakup fall-through juga. Itu juga membuat kedua kasus lebih nyaman dan mendokumentasikan diri sendiri, karena kita harus menambahkan label baru di C tetapi dapat menggunakancase
sebagai label di C #. Di C # kita bisa menghilangkanbelow_six
label dan menggunakangoto case 5
yang lebih jelas tentang apa yang kita lakukan. (Kami juga harus menambahkanbreak
untukdefault
, yang saya tinggalkan hanya untuk membuat kode C di atas jelas bukan kode C #).Singkatnya karena itu:
break
, untuk lebih mudah belajar bahasa oleh mereka yang akrab dengan bahasa yang sama, dan porting lebih mudah.goto
pendekatan berbasis yang sama untuk memukul blok yang sama dari yang berbedacase
label yang seperti yang digunakan dalam C. Itu hanya menggeneralisasikannya untuk beberapa kasus lain.goto
pendekatan berbasis itu lebih nyaman, dan lebih jelas, daripada di C, dengan memungkinkancase
pernyataan untuk bertindak sebagai label.Semua dalam semua, keputusan desain yang cukup masuk akal
* Beberapa bentuk BASIC akan memungkinkan seseorang untuk melakukan hal-hal seperti
GOTO (x AND 7) * 50 + 240
yang sementara rapuh dan karenanya kasus persuasif khusus untuk pelarangangoto
, memang berfungsi untuk menunjukkan setara bahasa yang lebih tinggi dari jenis cara kode tingkat yang lebih rendah dapat membuat lompatan berdasarkan aritmatika pada nilai, yang jauh lebih masuk akal ketika itu adalah hasil kompilasi daripada sesuatu yang harus dipertahankan secara manual. Implementasi Perangkat Duff khususnya cocok untuk kode mesin yang setara atau IL karena setiap blok instruksi akan sering memiliki panjang yang sama tanpa perlu penambahannop
pengisi.† Perangkat Duff muncul lagi di sini, sebagai pengecualian yang masuk akal. Fakta bahwa dengan pola itu dan yang serupa ada pengulangan operasi berfungsi untuk membuat penggunaan fall-through relatif jelas bahkan tanpa komentar eksplisit untuk efek itu.
sumber
Anda dapat 'membuka label kasus' http://www.blackwasp.co.uk/CSharpGoto.aspx
sumber
Mereka meninggalkan perilaku ini dengan desain untuk menghindari ketika itu tidak digunakan oleh kehendak tetapi menyebabkan masalah.
Ini dapat digunakan hanya jika tidak ada pernyataan di bagian case, seperti:
sumber
Mereka mengubah pernyataan beralih (dari C / Java / C ++) perilaku untuk c #. Saya kira alasannya adalah bahwa orang lupa tentang kejatuhan dan kesalahan terjadi. Satu buku yang saya baca dikatakan menggunakan goto untuk mensimulasikan, tetapi ini kedengarannya bukan solusi yang baik bagi saya.
sumber
goto case
. Keuntungannya daripada fallthrough adalah eksplisit. Bahwa beberapa orang di sini keberatangoto case
hanya menunjukkan bahwa mereka diindoktrinasi melawan "kebohongan" tanpa pemahaman tentang masalah ini dan tidak mampu berpikir sendiri. Ketika Dijkstra menulis "GOTO Dianggap Berbahaya", dia berbicara bahasa yang tidak memiliki cara lain untuk mengubah aliran kontrol.goto
dapat ketika mengoptimalkan kode?Anda dapat mencapai jatuh melalui seperti c ++ dengan kata kunci goto.
EX:
sumber
- Dokumentasi C # switch ()
sumber
Setelah setiap pernyataan kasus membutuhkan pernyataan break atau kebagian bahkan jika itu adalah kasus default.
sumber
Hanya catatan singkat untuk menambahkan bahwa kompiler untuk Xamarin benar-benar salah dan ini memungkinkan fallthrough. Seharusnya sudah diperbaiki, tetapi belum dirilis. Menemukan ini dalam beberapa kode yang benar-benar jatuh dan kompiler tidak mengeluh.
sumber
switch (C # Referensi) mengatakan
Jadi, Anda juga perlu menambahkan
break;
kedefault
bagian Anda , jika tidak maka akan ada kesalahan kompiler.sumber
C # tidak mendukung jatuh dengan pernyataan switch / case. Tidak yakin mengapa, tetapi benar-benar tidak ada dukungan untuk itu. Tautan
sumber
goto case
. Ini adalah pilihan disengaja, desain yang bijaksana oleh desainer C #.Anda lupa menambahkan "break;" pernyataan ke dalam case 3. Dalam kasus 2 Anda menulisnya ke dalam blok if. Karena itu coba ini:
sumber