Ini diperbolehkan:
int a, b, c;
a = b = c = 16;
string s = null;
while ((s = "Hello") != null) ;
Menurut pemahaman saya, penugasan s = ”Hello”;
seharusnya hanya menyebabkan “Hello”
ditugaskan s
, tetapi operasi seharusnya tidak mengembalikan nilai apa pun. Jika itu benar, maka ((s = "Hello") != null)
akan menghasilkan kesalahan, karena null
akan dibandingkan dengan apa pun.
Apa alasan di balik memungkinkan pernyataan tugas untuk mengembalikan nilai?
while ((s = GetWord()) != null) Process(s);
tidak).Jawaban:
Pemahaman Anda 100% salah. Bisakah Anda menjelaskan mengapa Anda percaya hal yang salah ini?
Pertama, pernyataan penugasan tidak menghasilkan nilai. Ekspresi penugasan menghasilkan nilai. Ekspresi penugasan adalah pernyataan hukum; hanya ada beberapa ekspresi yang merupakan pernyataan hukum dalam C #: menunggu ekspresi, contoh konstruksi, kenaikan, penurunan, pemanggilan dan penugasan ekspresi dapat digunakan di mana pernyataan diharapkan.
Hanya ada satu jenis ekspresi dalam C # yang tidak menghasilkan semacam nilai, yaitu, doa sesuatu yang diketikkan sebagai kembalian yang batal. (Atau, setara, menunggu tugas tanpa nilai hasil yang terkait.) Setiap jenis ekspresi lainnya menghasilkan nilai atau variabel atau referensi atau akses properti atau akses acara, dan sebagainya.
Perhatikan bahwa semua ekspresi yang sah sebagai pernyataan berguna untuk efek sampingnya . Itulah wawasan kunci di sini, dan saya pikir mungkin penyebab intuisi Anda bahwa tugas harus berupa pernyataan dan bukan ekspresi. Idealnya, kita memiliki satu efek samping per pernyataan, dan tidak ada efek samping dalam ekspresi. Ini adalah agak aneh bahwa kode sisi-mempengaruhi dapat digunakan dalam konteks ekspresi sama sekali.
Alasan di balik mengizinkan fitur ini adalah karena (1) sering nyaman dan (2) idiomatik dalam bahasa mirip-C.
Orang mungkin mencatat bahwa pertanyaannya telah diajukan: mengapa ini idiomatis dalam bahasa C-like?
Sayangnya, Dennis Ritchie tidak lagi dapat ditanyakan, tetapi tebakan saya adalah bahwa suatu tugas hampir selalu meninggalkan nilai yang baru saja ditetapkan dalam register. C adalah bahasa yang sangat "dekat dengan mesin". Tampaknya masuk akal dan sesuai dengan desain C bahwa ada fitur bahasa yang pada dasarnya berarti "tetap menggunakan nilai yang baru saja saya tetapkan". Sangat mudah untuk menulis generator kode untuk fitur ini; Anda tetap menggunakan register yang menyimpan nilai yang ditugaskan.
sumber
=
/==
kesalahan ketik, yang mudah diatasi dengan melarang menggunakan nilai=
kecuali tanda kurung. misalnya,if ((x = y))
atauif ((x = y) == true)
diizinkan tetapiif (x = y)
tidak.Apakah Anda tidak memberikan jawabannya? Ini untuk mengaktifkan secara tepat jenis konstruksi yang telah Anda sebutkan.
Kasus umum di mana properti dari operator penugasan ini digunakan adalah membaca baris dari file ...
sumber
return _myBackingField ?? (_myBackingField = CalculateBackingField());
Jauh lebih sedikit sekam daripada memeriksa nol dan menetapkan.Penggunaan ekspresi penugasan favorit saya adalah untuk properti yang diinisialisasi dengan malas.
sumber
??=
sintaksis.Untuk satu, itu memungkinkan Anda untuk rantai tugas Anda, seperti dalam contoh Anda:
Untuk yang lain, ini memungkinkan Anda untuk menetapkan dan memeriksa hasil dalam satu ekspresi:
Keduanya mungkin merupakan alasan yang meragukan, tetapi pasti ada orang yang menyukai konstruksi ini.
sumber
return (HttpContext.Current.Items["x"] = myvar);
Selain alasan yang telah disebutkan (tugas chaining, set-dan-uji dalam sementara loop, dll), untuk benar menggunakan
using
pernyataan Anda membutuhkan fitur ini:MSDN tidak menyarankan untuk mendeklarasikan objek sekali pakai di luar pernyataan menggunakan, karena ia akan tetap berada dalam ruang lingkup bahkan setelah dibuang (lihat artikel MSDN yang saya tautkan).
sumber
using
. Semua ini adalah hukum:using (X x = new X())
,using (x = new X())
,using (x)
. Namun, dalam contoh ini, isi pernyataan menggunakan adalah sintaks khusus yang sama sekali tidak bergantung pada tugas mengembalikan nilai -Font font3 = new Font("Arial", 10.0f)
bukan ekspresi dan tidak valid di tempat yang mengharapkan ekspresi.Saya ingin menguraikan poin spesifik yang dibuat Eric Lippert dalam jawabannya dan menyoroti pada kesempatan tertentu yang sama sekali belum disentuh oleh orang lain. Eric berkata:
Saya ingin mengatakan bahwa penugasan akan selalu meninggalkan nilai yang kami coba tetapkan untuk operan kiri kami. Bukan hanya "hampir selalu". Tetapi saya tidak tahu karena saya belum menemukan masalah ini dikomentari dalam dokumentasi. Secara teori mungkin ini merupakan prosedur yang diimplementasikan sangat efektif untuk "meninggalkan" dan tidak mengevaluasi kembali operan kiri, tetapi apakah itu efisien?
'Efisien' ya untuk semua contoh sejauh ini dibangun dalam jawaban utas ini. Tetapi efisien dalam hal properti dan pengindeks yang menggunakan aksesor dan atur? Tidak semuanya. Pertimbangkan kode ini:
Di sini kita memiliki properti, yang bahkan bukan pembungkus untuk variabel pribadi. Setiap kali dipanggil dia akan kembali benar, setiap kali seseorang mencoba untuk menetapkan nilainya dia tidak akan melakukan apa pun. Jadi, setiap kali properti ini dievaluasi, ia harus jujur. Mari lihat apa yang terjadi:
Coba tebak apa yang dicetaknya? Mencetak
Unexpected!!
. Ternyata, set accessor memang dipanggil, yang tidak melakukan apa-apa. Namun setelah itu, accessor get tidak pernah dipanggil sama sekali. Tugas hanya meninggalkanfalse
nilai yang kami coba tetapkan untuk properti kami. Danfalse
nilai ini adalah apa yang pernyataan if mengevaluasi.Saya akan menyelesaikannya dengan contoh dunia nyata yang membuat saya meneliti masalah ini. Saya membuat pengindeks yang merupakan pembungkus yang nyaman untuk koleksi (
List<string>
) yang dimiliki kelas saya sebagai variabel pribadi.Parameter yang dikirim ke pengindeks adalah string, yang harus diperlakukan sebagai nilai dalam koleksi saya. Pengakses get hanya akan mengembalikan benar atau salah jika nilai itu ada dalam daftar atau tidak. Jadi get accessor adalah cara lain untuk menggunakan
List<T>.Contains
metode ini.Jika set accessor pengindeks dipanggil dengan string sebagai argumen dan operan yang tepat adalah bool
true
, ia akan menambahkan parameter itu ke daftar. Tetapi jika parameter yang sama dikirim ke accessor dan operan yang tepat adalah boolfalse
, ia malah akan menghapus elemen dari daftar. Dengan demikian accessor set digunakan sebagai alternatif yang nyaman untuk keduanyaList<T>.Add
danList<T>.Remove
.Saya pikir saya punya "API" yang rapi dan kompak yang membungkus daftar dengan logika saya sendiri diimplementasikan sebagai gateway. Dengan bantuan pengindeks saja aku bisa melakukan banyak hal dengan beberapa set penekanan tombol. Misalnya, bagaimana saya bisa mencoba menambahkan nilai ke daftar saya dan memverifikasi bahwa itu ada di sana? Saya pikir ini adalah satu-satunya baris kode yang diperlukan:
Tapi seperti contoh saya sebelumnya menunjukkan, dapatkan accessor yang seharusnya melihat apakah nilainya benar-benar ada dalam daftar bahkan tidak dipanggil. The
true
nilai selalu tertinggal secara efektif menghancurkan logika apa pun saya telah diimplementasikan dalam mendapatkan accessor saya.sumber
Jika tugas tidak mengembalikan nilai, garis
a = b = c = 16
tidak akan berfungsi.Juga bisa menulis hal-hal seperti
while ((s = readLine()) != null)
terkadang bermanfaat.Jadi alasan di balik membiarkan tugas mengembalikan nilai yang diberikan, adalah untuk membiarkan Anda melakukan hal-hal itu.
sumber
=
terjadi dalam ekspresi yang tidak di-kurung (tidak termasuk parens dari pernyataan if / while itu sendiri). gcc memberikan peringatan semacam itu dan karenanya pada dasarnya menghilangkan kelas bug ini dari program C / C ++ yang dikompilasi dengannya. Ini memalukan bahwa penulis kompiler lain telah menaruh perhatian sangat sedikit untuk ini dan beberapa ide bagus lainnya di gcc.Saya pikir Anda salah paham bagaimana parser akan menafsirkan sintaks itu. Tugas akan dievaluasi terlebih dahulu , dan hasilnya kemudian akan dibandingkan dengan NULL, yaitu pernyataan itu setara dengan:
Seperti yang telah ditunjukkan orang lain, hasil penugasan adalah nilai yang diberikan. Saya merasa sulit membayangkan keuntungan memiliki
((s = "Hello") != null)
dan
tidak setara ...
sumber
Saya pikir alasan utama adalah kesamaan (disengaja) dengan C ++ dan C. Membuat operator assigment (dan banyak konstruksi bahasa lainnya) berperilaku seperti rekan-rekan C ++ mereka hanya mengikuti prinsip paling tidak mengejutkan, dan setiap programmer yang datang dari curly- lain bahasa braket dapat menggunakannya tanpa menghabiskan banyak pemikiran. Menjadi mudah diambil untuk programmer C ++ adalah salah satu tujuan desain utama untuk C #.
sumber
Karena dua alasan Anda termasuk dalam posting Anda
1) sehingga Anda dapat melakukan
a = b = c = 16
2) sehingga Anda dapat menguji apakah tugas berhasil
if ((s = openSomeHandle()) != null)
sumber
Fakta bahwa 'a ++' atau 'printf ("foo")' dapat berguna baik sebagai pernyataan mandiri atau sebagai bagian dari ekspresi yang lebih besar berarti bahwa C harus memungkinkan untuk kemungkinan bahwa hasil ekspresi mungkin atau mungkin tidak bekas. Mengingat hal itu, ada anggapan umum bahwa ekspresi yang mungkin berguna 'mengembalikan' nilai mungkin juga melakukannya. Penugasan tugas dapat sedikit "menarik" dalam C, dan bahkan lebih menarik dalam C ++, jika semua variabel yang dimaksud tidak memiliki tipe yang sama persis. Penggunaan seperti itu mungkin sebaiknya dihindari.
sumber
Contoh penggunaan yang bagus, saya menggunakan ini setiap saat:
sumber
Keuntungan tambahan yang tidak saya lihat dalam jawaban di sini, adalah bahwa sintaks untuk tugas didasarkan pada aritmatika.
Sekarang
x = y = b = c = 2 + 3
berarti sesuatu yang berbeda dalam aritmatika daripada bahasa gaya-C; dalam aritmatika ini merupakan pernyataan, kami menyatakan bahwa x sama dengan y dll dan dalam bahasa C-style itu adalah instruksi yang membuat x sama dengan y dll setelah dieksekusi.Ini mengatakan, masih ada hubungan yang cukup antara aritmatika dan kode yang tidak masuk akal untuk melarang apa yang alami dalam aritmatika kecuali ada alasan yang bagus. (Hal lain yang diambil oleh bahasa C-style dari penggunaan simbol equals adalah penggunaan == untuk perbandingan kesetaraan. Namun karena sebagian besar == mengembalikan nilai rangkaian seperti ini mustahil dilakukan.)
sumber
a = b = c
berartia
danb
danc
merupakan hal yang sama. Saat mempelajari bahasa gaya C, kami mempelajarinya setelahnyaa = b = c
,a
danb
sama seperti ituc
. Tentu saja ada perbedaan dalam semantik, seperti jawaban saya sendiri, tetapi masih ketika saya masih anak-anak saya belajar untuk memprogram dalam bahasa yang memang digunakan=
untuk tugas tetapi tidak membiarkana = b = c
ini tampak tidak masuk akal bagi saya, meskipun ...=
untuk perbandingan kesetaraan, jadi dalam hala = b = c
itu harus berarti apaa = b == c
artinya dalam bahasa gaya-C. Saya menemukan rantai yang diizinkan dalam C jauh lebih intuitif karena saya bisa menggambar analogi ke aritmatika.Saya suka menggunakan nilai pengembalian tugas ketika saya perlu memperbarui banyak hal dan mengembalikan apakah ada perubahan:
Berhati-hatilah. Anda mungkin berpikir Anda dapat mempersingkat ini menjadi ini:
Tetapi ini benar-benar akan berhenti mengevaluasi atau pernyataan setelah menemukan yang pertama benar. Dalam hal ini berarti berhenti menetapkan nilai berikutnya setelah menetapkan nilai pertama yang berbeda.
Lihat https://dotnetfiddle.net/e05Rh8 untuk bermain-main dengan ini
sumber