Sesuatu yang saya kenal untuk sementara waktu tetapi tidak pernah dipertimbangkan adalah bahwa dalam sebagian besar bahasa adalah mungkin untuk memberikan prioritas kepada operator dalam pernyataan if berdasarkan pesanan mereka. Saya sering menggunakan ini sebagai cara untuk mencegah pengecualian referensi nol, misalnya:
if (smartphone != null && smartphone.GetSignal() > 50)
{
// Do stuff
}
Dalam hal ini kode akan menghasilkan pengecekan pertama bahwa objek tersebut tidak nol dan kemudian menggunakan objek ini mengetahui bahwa itu ada. Bahasa ini pintar karena ia tahu bahwa jika pernyataan pertama jika salah maka tidak ada gunanya mengevaluasi pernyataan kedua sehingga pengecualian referensi nol tidak pernah dilempar. Ini bekerja sama untuk and
dan or
operator.
Ini dapat berguna dalam situasi lain juga seperti memeriksa bahwa indeks berada dalam batas-batas array dan teknik seperti itu dapat dilakukan dalam berbagai bahasa, setahu saya: Java, C #, C ++, Python dan Matlab.
Pertanyaan saya adalah: Apakah kode semacam ini merupakan representasi praktik buruk? Apakah praktik buruk ini muncul dari beberapa masalah teknis tersembunyi (yaitu hal ini pada akhirnya dapat menyebabkan kesalahan) atau apakah hal itu menyebabkan masalah keterbacaan untuk programmer lain? Mungkinkah ini membingungkan?
||
hubung singkat operator , operator|
(pipa tunggal) tidak (&&
dan&
berperilaku sama). Karena operator hubung singkat lebih sering digunakan, saya sarankan untuk menandai penggunaan yang bukan hubung singkat dengan komentar untuk menjelaskan mengapa Anda memerlukan perilaku mereka (kebanyakan hal seperti pemanggilan metode yang semuanya harus terjadi).&
atau|
untuk memastikan efek samping terjadi adalah sesuatu yang saya katakan tentu saja adalah praktik yang buruk: Tidak akan dikenakan biaya apa pun untuk melakukan panggilan metode pada jalurnya sendiri.&&
adalah hubungan arus pendek dan&
tidak, tapi mereka operator yang sangat berbeda.&&
adalah logis "dan";&
adalah bitwise "and". Adalah mungkin untukx && y
menjadi benar sementarax & y
itu salah.Jawaban:
Tidak, ini bukan praktik buruk. Mengandalkan hubungan arus pendek kondisi adalah teknik bermanfaat yang diterima secara luas - selama Anda menggunakan bahasa yang menjamin perilaku ini (yang mencakup sebagian besar bahasa modern).
Contoh kode Anda cukup jelas dan, memang, itu sering kali merupakan cara terbaik untuk menulisnya. Alternatif (seperti
if
pernyataan bersarang ) akan menjadi berantakan, lebih rumit, dan karenanya lebih sulit untuk dipahami.Beberapa peringatan:
Gunakan akal sehat tentang kompleksitas dan keterbacaan
Berikut ini bukan ide yang bagus:
Seperti banyak cara "pintar" untuk mengurangi garis-garis dalam kode Anda, terlalu mengandalkan teknik ini dapat menghasilkan kode yang mudah dipahami dan rentan kesalahan.
Jika Anda perlu menangani kesalahan, seringkali ada cara yang lebih baik
Contoh Anda baik-baik saja asalkan kondisi program normal untuk ponsel cerdas adalah nol. Namun, jika ini adalah kesalahan, Anda harus memasukkan penanganan kesalahan yang lebih cerdas.
Hindari pemeriksaan berulang dengan kondisi yang sama
Seperti yang ditunjukkan Neil, banyak pemeriksaan berulang dari variabel yang sama dalam kondisi yang berbeda kemungkinan besar merupakan bukti cacat desain. Jika Anda memiliki sepuluh pernyataan yang berbeda ditaburi melalui kode Anda yang tidak boleh dijalankan jika
smartphone
ininull
, mempertimbangkan apakah ada cara yang lebih baik untuk menangani ini selain memeriksa variabel setiap kali.Ini bukan tentang hubungan arus pendek secara khusus; itu masalah yang lebih umum dari kode berulang. Namun, perlu disebutkan, karena cukup umum untuk melihat kode yang memiliki banyak pernyataan berulang seperti pernyataan contoh Anda.
sumber
Katakanlah Anda menggunakan bahasa gaya-C tanpa
&&
dan perlu melakukan kode yang sama seperti dalam pertanyaan Anda.Kode Anda adalah:
Pola ini akan banyak muncul.
Sekarang bayangkan versi 2.0 dari bahasa hipotetis kita memperkenalkan
&&
. Pikirkan betapa kerennya Anda!&&
adalah cara idiomatis yang diakui untuk melakukan apa yang dilakukan oleh contoh saya di atas. Ini bukan praktik buruk untuk menggunakannya, memang itu praktik buruk untuk tidak menggunakannya dalam kasus-kasus seperti di atas: Seorang programmer yang berpengalaman dalam bahasa seperti itu akan bertanya-tanya di manaelse
itu atau alasan lain untuk tidak melakukan sesuatu dengan cara normal.Tidak, Anda pintar, karena Anda tahu bahwa jika pernyataan pertama salah maka tidak ada gunanya mengevaluasi pernyataan kedua. Bahasa ini bodoh seperti sekotak batu dan melakukan apa yang Anda perintahkan. (John McCarthy bahkan lebih pintar, dan menyadari bahwa evaluasi hubung singkat akan menjadi hal yang berguna bagi bahasa untuk dimiliki).
Perbedaan antara Anda menjadi pintar dan bahasa menjadi pintar adalah penting, karena praktik yang baik dan buruk sering kali membuat Anda menjadi sepintar yang diperlukan, tetapi tidak lagi pintar.
Mempertimbangkan:
Ini memperluas kode Anda untuk memeriksa a
range
. Jikarange
isinya nol maka ia mencoba menyetelnya dengan panggilanGetRange()
meskipun mungkin gagal jadirange
mungkin masih nol. Jika rentang tidak nol setelah ini, danValid
kemudianMinSignal
propertinya digunakan, jika tidak maka default50
digunakan.Ini tergantung pada
&&
juga, tetapi meletakkannya dalam satu baris mungkin terlalu pintar (saya tidak 100% yakin saya sudah benar, dan saya tidak akan memeriksa ulang karena fakta itu menunjukkan maksud saya).Bukan
&&
itu masalahnya di sini, tetapi kemampuan untuk menggunakannya untuk menempatkan banyak dalam satu ekspresi (hal yang baik) meningkatkan kemampuan kita untuk menulis ekspresi yang sulit dipahami secara tidak perlu (hal yang buruk).Juga:
Di sini saya menggabungkan penggunaan
&&
dengan memeriksa nilai-nilai tertentu. Ini tidak realistis dalam hal sinyal telepon, tetapi muncul dalam kasus lain. Di sini, ini adalah contoh saya tidak cukup pintar. Jika saya melakukan hal berikut:Saya akan mendapatkan baik keterbacaan dan juga kemungkinan dalam kinerja (beberapa panggilan ke
GetSignal()
kemungkinan tidak akan dioptimalkan).Lagi-lagi, masalahnya bukan
&&
pada mengambil palu itu dan menganggap yang lainnya sebagai paku; tidak menggunakannya biarkan aku melakukan sesuatu yang lebih baik daripada menggunakannya.Kasus terakhir yang menjauh dari praktik terbaik adalah:
Dibandingkan dengan:
Argumen klasik tentang mengapa kita mungkin mendukung yang terakhir adalah bahwa ada beberapa efek samping dalam menilai
b
yang kita inginkan apakaha
benar atau tidak. Saya tidak setuju tentang itu: Jika efek samping itu sangat penting, maka wujudkan dalam baris kode yang terpisah.Namun, dalam hal efisiensi, keduanya kemungkinan lebih baik. Yang pertama jelas akan mengeksekusi lebih sedikit kode (tidak menilai
b
sama sekali dalam satu jalur kode) yang dapat menyelamatkan kita berapa pun waktu yang diperlukan untuk menilaib
.Yang pertama juga memiliki satu cabang lagi. Pertimbangkan jika kita menulis ulang dalam bahasa C-style hipotetis kita tanpa
&&
:Ekstra
if
itu tersembunyi dalam penggunaan kita&&
, tetapi masih ada. Dan karena itu adalah kasus di mana ada prediksi cabang terjadi dan karena itu berpotensi salah prediksi cabang.Untuk alasan itu,
if(a & b)
kode dapat lebih efisien dalam beberapa kasus.Di sini saya akan mengatakan bahwa
if(a && b)
masih merupakan pendekatan praktik terbaik untuk memulai dengan: Lebih umum, satu-satunya yang layak dalam beberapa kasus (di manab
kesalahan jikaa
salah) dan lebih cepat lebih cepat daripada tidak. Perlu dicatat bahwaif(a & b)
sering kali optimasi yang berguna untuk itu dalam kasus tertentu.sumber
try
metode yang kembalitrue
jika sukses, apa yang Anda pikirkanif (TryThis || TryThat) { success } else { failure }
? Ini adalah idiom yang kurang umum, tetapi saya tidak tahu bagaimana menyelesaikan hal yang sama tanpa duplikasi kode atau menambahkan flag. Sementara memori yang diperlukan untuk sebuah bendera tidak menjadi masalah, dari sudut pandang analisis, menambahkan bendera secara efektif membagi kode menjadi dua alam semesta paralel - satu berisi pernyataan yang dapat dijangkau ketika bendera itutrue
dan satu pernyataan lainnya dapat dijangkau ketika itufalse
. Terkadang bagus dari perspektif KERING, tapi menjengkelkan.if(TryThis || TryThat)
.if {} else if {} else if {} else {}
daripada struktur kontrol bersarang sepertiif { if {} else {} } else {}
, bahkan jika itu berarti mengevaluasi ekspresi yang sama lebih dari sekali. Linus Torvalds mengatakan sesuatu yang serupa: "jika Anda membutuhkan lebih dari 3 level indentasi, Anda tetap kacau". Mari kita anggap itu sebagai suara untuk operator hubung singkat.Anda dapat mengambil ini lebih jauh, dan dalam beberapa bahasa itu adalah cara idiomatis untuk melakukan sesuatu: Anda dapat menggunakan evakuasi hubung singkat di luar pernyataan bersyarat juga, dan mereka menjadi bentuk pernyataan bersyarat sendiri. Misalnya dalam Perl, itu adalah idiomatik bagi fungsi untuk mengembalikan sesuatu yang salah pada kegagalan (dan sesuatu yang benar pada kesuksesan), dan sesuatu seperti
idiom.
open
mengembalikan sesuatu yang bukan nol pada kesuksesan (sehinggadie
bagian setelahor
tidak pernah terjadi), tetapi tidak didefinisikan pada kegagalan, dan kemudian panggilan untukdie
itu terjadi (itulah cara Perl untuk mengajukan pengecualian).Itu bagus dalam bahasa di mana semua orang melakukannya.
sumber
unless
, dan postfix conditional (send_mail($address) if defined $address
).send_mail(address) if address
Walaupun saya umumnya setuju dengan jawaban dan1111 , ada satu kasus penting yang tidak dibahas: kasus di mana
smartphone
digunakan secara bersamaan. Dalam skenario itu, pola semacam ini merupakan sumber yang sulit ditemukan bug.Masalahnya adalah bahwa evaluasi hubung singkat tidak atomik. Utas Anda dapat memeriksa apakah
smartphone
itunull
, utas lain dapat masuk dan membatalkannya, lalu utas Anda segera mencoba untukGetSignal()
dan meledak.Tapi tentu saja, itu hanya melakukan itu ketika bintang-bintang menyelaraskan dan waktu benang Anda adalah hanya benar.
Jadi, meskipun kode semacam ini sangat umum dan bermanfaat, penting untuk mengetahui peringatan ini sehingga Anda dapat secara akurat mencegah bug jahat ini.
sumber
Ada banyak situasi di mana saya ingin memeriksa satu kondisi terlebih dahulu, dan hanya ingin memeriksa kondisi kedua jika kondisi pertama berhasil. Kadang-kadang murni untuk efisiensi (karena tidak ada gunanya memeriksa kondisi kedua jika yang pertama sudah gagal), kadang-kadang karena kalau tidak program saya akan crash (cek Anda untuk NULL pertama), kadang-kadang karena kalau tidak hasilnya akan mengerikan. (JIKA (saya ingin mencetak dokumen) DAN (mencetak dokumen gagal)) KEMUDIAN menampilkan pesan kesalahan - Anda tidak ingin mencetak dokumen dan memeriksa apakah pencetakan gagal jika Anda tidak ingin mencetak dokumen di tempat pertama!
Jadi ada kebutuhan BESAR untuk evaluasi hubung singkat ekspresi logis dijamin. Itu tidak hadir di Pascal (yang memiliki evaluasi hubung singkat tidak dijamin) yang merupakan rasa sakit utama; Saya pertama kali melihatnya di Ada dan C.
Jika Anda benar-benar berpikir ini adalah "praktik buruk", maka silakan ambil sedikit kode yang cukup kompleks, tulis ulang sehingga akan berfungsi dengan baik tanpa evaluasi hubungan pendek, dan kembali dan beri tahu kami bagaimana Anda menyukainya. Ini seperti diberi tahu bahwa pernapasan menghasilkan karbon dioksida dan menanyakan apakah pernapasan adalah praktik yang buruk. Cobalah tanpanya selama beberapa menit.
sumber
Satu pertimbangan, tidak disebutkan dalam jawaban lain: kadang-kadang memiliki pemeriksaan ini dapat mengisyaratkan kemungkinan refactoring ke pola desain Obyek Null . Sebagai contoh:
Bisa disederhanakan menjadi:
Jika
currentUser
default untuk beberapa 'pengguna anonim' atau 'pengguna nol' dengan implementasi mundur jika pengguna tidak masuk.Tidak selalu bau kode, tetapi sesuatu yang perlu dipertimbangkan.
sumber
Saya akan menjadi tidak populer dan berkata Ya, ini adalah praktik yang buruk. JIKA fungsionalitas kode Anda bergantung padanya untuk menerapkan logika kondisional.
yaitu
itu bagus, tapi
buruk, dan pasti tidak ada yang akan setuju?
Alasan:
Kesalahan kode Anda jika kedua belah pihak dievaluasi daripada hanya tidak melakukan logika. Telah diperdebatkan bahwa ini bukan masalah, operator 'dan' didefinisikan dalam c # jadi artinya jelas. Namun, saya berpendapat bahwa ini tidak benar.
Alasan 1. Historis
Pada dasarnya Anda mengandalkan (apa yang awalnya dimaksudkan) pengoptimalan kompiler untuk menyediakan fungsionalitas dalam kode Anda.
Meskipun dalam c # spesifikasi bahasa menjamin operator boolean 'dan' melakukan hubungan arus pendek. Ini tidak berlaku untuk semua bahasa dan kompiler.
wikipeda mencantumkan berbagai bahasa dan pendapat mereka tentang hubungan pendek http://en.wikipedia.org/wiki/Short-circuit_evaluation karena Anda dapat, ada banyak pendekatan. Dan beberapa masalah tambahan saya tidak pergi ke sini.
Secara khusus, FORTRAN, Pascal dan Delphi memiliki flag compiler yang mengubah perilaku ini.
Oleh karena itu: Anda mungkin menemukan kode Anda berfungsi ketika dikompilasi untuk rilis, tetapi tidak untuk debug atau dengan kompiler alternatif dll.
Alasan 2. Perbedaan bahasa
Seperti yang Anda lihat dari wikipedia, tidak semua bahasa menggunakan pendekatan yang sama untuk hubungan pendek, bahkan ketika mereka menentukan bagaimana operator boolean harus menanganinya.
Khususnya operator 'dan' VB.Net tidak mengalami hubungan pendek dan TSQL memiliki beberapa kekhasan.
Mengingat bahwa 'solusi' modern kemungkinan akan mencakup sejumlah bahasa, seperti javascript, regex, xpath dll. Anda harus mempertimbangkan ini saat membuat fungsi kode Anda jelas.
Alasan 3. Perbedaan dalam suatu bahasa
Misalnya inline VB jika berperilaku berbeda dari jika. Sekali lagi dalam aplikasi modern, kode Anda mungkin bergerak di antara, misalnya kode di belakang dan formulir web inline, Anda harus menyadari perbedaan makna.
Alasan 4. Contoh spesifik Anda nol memeriksa parameter fungsi
Ini adalah contoh yang sangat bagus. Dalam hal itu adalah sesuatu yang dilakukan setiap orang, tetapi sebenarnya merupakan praktik yang buruk ketika Anda memikirkannya.
Ini sangat umum untuk melihat jenis cek karena mengurangi baris kode Anda. Saya ragu ada orang yang akan membawanya dalam tinjauan kode. (dan jelas dari komentar dan penilaian Anda dapat melihatnya populer!)
Namun, itu gagal praktik terbaik karena lebih dari satu alasan!
Its sangat jelas dari berbagai sumber, bahwa praktek terbaik adalah untuk memeriksa parameter untuk validitas sebelum Anda menggunakannya dan melemparkan pengecualian jika mereka tidak valid. Cuplikan kode Anda tidak menunjukkan kode di sekitarnya, tetapi Anda mungkin harus melakukan sesuatu seperti:
Anda memanggil fungsi dari dalam pernyataan bersyarat. Meskipun nama fungsi Anda menyiratkan bahwa itu hanya menghitung nilai, itu dapat menyembunyikan efek samping. misalnya
praktik terbaik akan menyarankan:
Jika Anda menerapkan praktik terbaik ini ke kode Anda, Anda dapat melihat bahwa peluang Anda untuk mencapai kode yang lebih pendek melalui penggunaan kondisional tersembunyi menghilang. Praktik terbaik adalah melakukan sesuatu , untuk menangani kasus di mana ponsel cerdas adalah nol.
Saya pikir Anda akan menemukan bahwa ini juga berlaku untuk penggunaan umum lainnya. Anda harus ingat kami juga memiliki:
kondisional sebaris
dan null koalesensi
yang menyediakan fungsionalitas panjang kode pendek serupa dengan lebih jelas
banyak tautan untuk mendukung semua poin saya:
https://stackoverflow.com/questions/1158614/what-is-the-best-practice-in-case-one-argument-is-null
Validasi parameter konstruktor dalam C # - Praktik terbaik
Haruskah seseorang memeriksa nol jika dia tidak mengharapkan nol?
https://msdn.microsoft.com/en-us/library/seyhszts%28v=vs.110%29.aspx
https://stackoverflow.com/questions/1445867/why-would-a-language-not-use-short-circuit-evaluation
http://orcharddojo.net/orchard-resources/Library/DevelopmentGuidelines/BestPractices/CSharp
contoh flag kompiler yang mempengaruhi hubungan pendek:
Fortran: "sce | nosce Secara default, kompiler melakukan evaluasi hubung singkat dalam ekspresi logis yang dipilih menggunakan aturan XL Fortran. Menentukan sce memungkinkan kompiler menggunakan aturan Fortran non-XL. Kompilator akan melakukan evaluasi hubung singkat jika aturan saat ini mengizinkannya Defaultnya adalah nosce. "
Pascal: "B - Sebuah flag directive yang mengontrol evaluasi hubung singkat. Lihat Urutan evaluasi operan operator diad untuk informasi lebih lanjut tentang evaluasi hubung singkat. Lihat juga opsi kompilator -sc untuk kompiler baris perintah untuk informasi lebih lanjut ( didokumentasikan dalam Manual Pengguna). "
Delphi: "Delphi mendukung evaluasi hubung singkat secara default. Delphi dapat dimatikan menggunakan arahan kompiler {$ BOOLEVAL OFF}."
sumber