Kata kunci hasil adalah salah satu kata kunci dalam C # yang terus membingungkan saya, dan saya tidak pernah yakin bahwa saya menggunakannya dengan benar.
Dari dua potongan kode berikut, mana yang lebih disukai dan mengapa?
Versi 1: Menggunakan pengembalian hasil
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
foreach (Product product in products)
{
yield return product;
}
}
}
Versi 2: Kembalikan daftar
public static IEnumerable<Product> GetAllProducts()
{
using (AdventureWorksEntities db = new AdventureWorksEntities())
{
var products = from product in db.Product
select product;
return products.ToList<Product>();
}
}
c#
yield-return
senfo
sumber
sumber
yield
terikatIEnumerable<T>
dan jenisnya. Ini dalam evaluasi malas entah bagaimanayield return
jika kode yang berulang melalui hasilGetAllProducts()
memungkinkan pengguna kesempatan untuk membatalkan pemrosesan lebih awal.Jawaban:
Saya cenderung menggunakan imbal hasil ketika saya menghitung item berikutnya dalam daftar (atau bahkan grup item berikutnya).
Menggunakan Versi 2 Anda, Anda harus memiliki daftar lengkap sebelum kembali. Dengan menggunakan imbal hasil, Anda benar-benar hanya perlu memiliki item berikutnya sebelum kembali.
Di antara hal-hal lain, ini membantu menyebarkan biaya perhitungan perhitungan yang kompleks dalam kerangka waktu yang lebih besar. Misalnya, jika daftar terhubung ke GUI dan pengguna tidak pernah pergi ke halaman terakhir, Anda tidak pernah menghitung item akhir dalam daftar.
Kasus lain di mana imbal hasil lebih disukai adalah jika IEnumerable mewakili set yang tak terbatas. Pertimbangkan daftar Nomor Utama, atau daftar nomor acak tanpa batas. Anda tidak pernah dapat mengembalikan IEnumerable penuh sekaligus, jadi Anda menggunakan imbal hasil untuk mengembalikan daftar secara bertahap.
Dalam contoh khusus Anda, Anda memiliki daftar lengkap produk, jadi saya akan menggunakan Versi 2.
sumber
Yield return
tampaknya menjadi singkatan untuk menulis kelas iterator kustom Anda sendiri (mengimplementasikan IEnumerator). Karenanya, manfaat yang disebutkan juga berlaku untuk kelas iterator khusus. Pokoknya, kedua konstruksi menjaga keadaan menengah. Dalam bentuk yang paling sederhana, ini tentang memegang referensi ke objek saat ini.Mengisi daftar sementara seperti mengunduh seluruh video, sedangkan menggunakan
yield
seperti streaming video itu.sumber
Sebagai contoh konseptual untuk memahami kapan Anda harus menggunakan
yield
, katakanlah metodeConsumeLoop()
memproses item yang dikembalikan / dihasilkan olehProduceList()
:Tanpa
yield
, panggilan keProduceList()
mungkin memakan waktu lama karena Anda harus melengkapi daftar sebelum kembali:Menggunakan
yield
, itu diatur ulang, semacam bekerja "secara paralel":Dan terakhir, seperti banyak yang sudah disarankan sebelumnya, Anda harus menggunakan Versi 2 karena Anda sudah memiliki daftar yang lengkap.
sumber
Saya tahu ini adalah pertanyaan lama, tetapi saya ingin menawarkan satu contoh bagaimana kata kunci hasil dapat digunakan secara kreatif. Saya benar - benar mendapat manfaat dari teknik ini. Semoga ini bisa menjadi bantuan bagi siapa pun yang menemukan pertanyaan ini.
Catatan: Jangan menganggap kata kunci hasil sebagai hanya cara lain untuk membangun koleksi. Sebagian besar dari kekuatan hasil datang dalam kenyataan bahwa eksekusi dijeda dalam metode atau properti Anda hingga kode panggilan beralih ke nilai berikutnya. Inilah contoh saya:
Dengan menggunakan kata kunci hasil (di samping implementasi coribine Coribine Caliburn.Micro karya Rob Eisenburg ) memungkinkan saya untuk mengekspresikan panggilan asinkron ke layanan web seperti ini:
Apa yang akan dilakukan adalah mengaktifkan BusyIndicator saya, memanggil metode Login di layanan web saya, mengatur bendera IsLoggedIn saya ke nilai kembali, dan kemudian mematikan BusyIndicator kembali.
Begini cara kerjanya: IResult memiliki metode Execute dan acara Selesai. Caliburn.Micro mengambil IEnumerator dari panggilan ke HandleButtonClick () dan meneruskannya ke metode Coroutine.BeginExecute. Metode BeginExecute mulai iterasi melalui IResult. Ketika IResult pertama dikembalikan, eksekusi dijeda di dalam HandleButtonClick (), dan BeginExecute () melampirkan pengendali acara ke acara Selesai dan panggilan Execute (). IResult.Execute () dapat melakukan tugas sinkron atau asinkron dan menjalankan acara Selesai saat selesai.
LoginResult terlihat seperti ini:
Mungkin membantu untuk mengatur sesuatu seperti ini dan melangkah melalui eksekusi untuk menonton apa yang terjadi.
Semoga ini bisa membantu seseorang! Saya benar-benar menikmati menjelajahi berbagai cara menghasilkan yang dapat digunakan.
sumber
yield
cara ini. Sepertinya cara yang elegan untuk meniru pola async / menunggu (yang saya asumsikan akan digunakan alih-alihyield
jika ini ditulis ulang hari ini). Pernahkah Anda menemukan bahwa penggunaan materi iklan yang kreatifyield
ini menghasilkan (tanpa bermaksud kata-kata) yang semakin berkurang selama bertahun-tahun karena C # telah berkembang sejak Anda menjawab pertanyaan ini? Atau apakah Anda masih datang dengan kasus penggunaan cerdas modern seperti ini? Dan jika demikian, maukah Anda berbagi skenario menarik lainnya untuk kami?Ini akan tampak seperti saran yang aneh, tetapi saya belajar bagaimana menggunakan
yield
kata kunci dalam C # dengan membaca presentasi tentang generator dengan Python: http://www.dabeaz.com/generators/Generators.pdf David M. Beazley . Anda tidak perlu tahu banyak tentang Python untuk memahami presentasi - saya tidak. Saya merasa sangat membantu dalam menjelaskan tidak hanya bagaimana generator bekerja tetapi mengapa Anda harus peduli.sumber
Pengembalian hasil bisa sangat kuat untuk algoritma di mana Anda harus beralih melalui jutaan objek. Pertimbangkan contoh berikut di mana Anda perlu menghitung kemungkinan perjalanan untuk berbagi perjalanan. Pertama, kami menghasilkan perjalanan yang mungkin:
Kemudian ulangi setiap perjalanan:
Jika Anda menggunakan Daftar alih-alih hasil, Anda perlu mengalokasikan 1 juta objek ke memori (~ 190mb) dan contoh sederhana ini akan membutuhkan ~ 1400ms untuk dijalankan. Namun, jika Anda menggunakan hasil, Anda tidak perlu meletakkan semua objek temp ini ke memori dan Anda akan mendapatkan kecepatan algoritma yang jauh lebih cepat: contoh ini hanya akan memakan waktu ~ 400 ms untuk berjalan tanpa konsumsi memori sama sekali.
sumber
yield
bekerja di bawah penutup dengan menerapkan mesin negara secara internal. Inilah jawaban SO dengan 3 posting blog MSDN terperinci yang menjelaskan implementasinya dengan sangat rinci. Ditulis oleh Raymond Chen @ MSFTDua potong kode ini benar-benar melakukan dua hal yang berbeda. Versi pertama akan menarik anggota saat Anda membutuhkannya. Versi kedua akan memuat semua hasil ke dalam memori sebelum Anda mulai melakukan apa pun dengannya.
Tidak ada jawaban benar atau salah untuk yang satu ini. Mana yang lebih disukai hanya tergantung pada situasinya. Misalnya, jika ada batas waktu untuk menyelesaikan kueri dan Anda perlu melakukan sesuatu yang semi-rumit dengan hasilnya, versi kedua bisa lebih disukai. Namun waspadalah terhadap hasil yang besar, terutama jika Anda menjalankan kode ini dalam mode 32-bit. Saya telah digigit oleh pengecualian OutOfMemory beberapa kali ketika melakukan metode ini.
Namun, hal utama yang perlu diingat adalah: perbedaannya dalam efisiensi. Dengan demikian, Anda mungkin harus memilih yang mana yang membuat kode Anda lebih mudah dan mengubahnya hanya setelah membuat profil.
sumber
Yield memiliki dua kegunaan besar
Ini membantu untuk menyediakan iterasi khusus tanpa membuat koleksi temp. (memuat semua data dan perulangan)
Ini membantu untuk melakukan iterasi stateful. ( Streaming)
Di bawah ini adalah video sederhana yang saya buat dengan demonstrasi penuh untuk mendukung dua poin di atas
http://www.youtube.com/watch?v=4fju3xcm21M
sumber
Inilah yang dikatakan Chris Sells tentang pernyataan itu dalam Bahasa Pemrograman C # ;
sumber
F().Any()
- ini akan kembali setelah mencoba untuk menghitung hasil pertama saja. Secara umum, Anda tidak boleh mengandalkan padaIEnumerable yield
untuk mengubah status program, karena itu mungkin tidak benar-benar dipicuDengan asumsi produk Anda kelas LINQ menggunakan hasil yang sama untuk penghitungan / iterasi, versi pertama lebih efisien karena hanya menghasilkan satu nilai setiap kali iterasi berakhir.
Contoh kedua adalah mengubah enumerator / iterator ke daftar dengan metode ToList (). Ini berarti secara manual beralih ke semua item dalam enumerator dan kemudian mengembalikan daftar datar.
sumber
Ini agak tidak penting, tetapi karena pertanyaannya ditandai praktik terbaik, saya akan melanjutkan dan memasukkan dua sen saya. Untuk hal semacam ini saya lebih suka membuatnya menjadi properti:
Tentu, ini sedikit lebih boiler-plate, tetapi kode yang menggunakan ini akan terlihat jauh lebih bersih:
vs.
Catatan: Saya tidak akan melakukan ini untuk metode apa pun yang mungkin memerlukan waktu untuk melakukan pekerjaan mereka.
sumber
Dan bagaimana dengan ini?
Saya kira ini jauh lebih bersih. Saya tidak punya VS2008 di tangan untuk memeriksa, meskipun. Dalam kasus apa pun, jika Produk mengimplementasikan IEnumerable (seperti yang terlihat - digunakan dalam pernyataan foreach), saya akan mengembalikannya secara langsung.
sumber
Saya akan menggunakan versi 2 kode dalam kasus ini. Karena Anda memiliki daftar lengkap produk yang tersedia dan itulah yang diharapkan oleh "konsumen" dari pemanggilan metode ini, Anda harus mengirim informasi lengkap kembali ke pemanggil.
Jika penelepon dari metode ini memerlukan "satu" informasi pada satu waktu dan konsumsi informasi berikutnya adalah berdasarkan permintaan, maka akan bermanfaat untuk menggunakan pengembalian hasil yang akan memastikan perintah eksekusi akan dikembalikan ke penelepon ketika satu unit informasi tersedia.
Beberapa contoh di mana seseorang dapat menggunakan pengembalian hasil adalah:
Untuk menjawab pertanyaan Anda, saya akan menggunakan versi 2.
sumber
Kembalikan daftar secara langsung. Manfaat:
Daftar ini dapat digunakan kembali. (iterator tidak)sebenarnya tidak benar, Terima kasih JonAnda harus menggunakan iterator (hasil) dari saat Anda berpikir Anda mungkin tidak perlu mengulangi sampai ke akhir daftar, atau ketika itu tidak memiliki akhir. Misalnya, panggilan klien akan mencari produk pertama yang memenuhi beberapa predikat, Anda dapat mempertimbangkan menggunakan iterator, meskipun itu adalah contoh yang dibuat-buat, dan mungkin ada cara yang lebih baik untuk mencapainya. Pada dasarnya, jika Anda tahu sebelumnya bahwa seluruh daftar perlu dihitung, lakukan saja di depan. Jika Anda berpikir itu tidak akan terjadi, maka pertimbangkan untuk menggunakan versi iterator.
sumber
Frasa kunci hasil pengembalian digunakan untuk memelihara mesin keadaan untuk koleksi tertentu. Di mana pun CLR melihat frasa kunci hasil pengembalian yang digunakan, CLR mengimplementasikan pola Enumerator ke potongan kode itu. Jenis implementasi ini membantu pengembang dari semua jenis pipa leding yang harus kami lakukan jika tidak ada kata kunci.
Misalkan jika pengembang memfilter beberapa koleksi, iterasi koleksi tersebut dan kemudian ekstrak objek-objek itu dalam beberapa koleksi baru. Pipa jenis ini cukup monoton.
Lebih lanjut tentang kata kunci di sini di artikel ini .
sumber
Penggunaan hasil mirip dengan kata kunci kembali , kecuali bahwa itu akan mengembalikan pembangkit . Dan objek generator hanya akan melintas satu kali .
hasil memiliki dua manfaat:
Ada penjelasan lain yang jelas mungkin bisa membantu Anda.
sumber