Untuk apa kata kunci hasil dalam C #?

829

Dalam pertanyaan Bagaimana Saya Hanya Mengekspos Bagian dari IList <> salah satu jawaban memiliki potongan kode berikut:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

Apa yang dilakukan kata kunci hasil di sana? Saya telah melihatnya direferensikan di beberapa tempat, dan satu pertanyaan lain, tapi saya belum tahu apa yang sebenarnya dilakukannya. Saya terbiasa berpikir tentang hasil dalam arti satu thread menghasilkan yang lain, tetapi itu tampaknya tidak relevan di sini.

Herms
sumber
Hanya tautan MSDN tentang hal itu di sini msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
Pengembang
14
Ini tidak mengejutkan. Kebingungan datang dari fakta bahwa kita dikondisikan untuk melihat "kembali" sebagai output fungsi sementara didahului oleh "hasil" bukan.
Larry
4
Saya sudah membaca dokumen tapi saya rasa saya masih tidak mengerti :(
Ortund

Jawaban:

737

Kata yieldkunci sebenarnya cukup banyak di sini.

Fungsi mengembalikan objek yang mengimplementasikan IEnumerable<object>antarmuka. Jika fungsi panggilan mulai di foreachatas objek ini, fungsi dipanggil lagi sampai "menghasilkan". Ini adalah gula sintaksis yang diperkenalkan dalam C # 2.0 . Di versi sebelumnya Anda harus membuat objek Anda sendiri IEnumerabledan IEnumeratormelakukan hal-hal seperti ini.

Cara termudah untuk memahami kode seperti ini adalah mengetikkan contoh, mengatur beberapa breakpoint, dan melihat apa yang terjadi. Cobalah melangkah melalui contoh ini:

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

Ketika Anda melangkah melalui contoh, Anda akan menemukan panggilan pertama untuk Integers()kembali 1. Panggilan kedua kembali 2dan saluran yield return 1tidak dieksekusi lagi.

Ini adalah contoh kehidupan nyata:

public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
    using (var connection = CreateConnection())
    {
        using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
        {
            command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return make(reader);
                }
            }
        }
    }
}
Mendelt
sumber
113
Dalam hal ini yang akan lebih mudah, saya hanya menggunakan integer di sini untuk menunjukkan cara kerja imbal hasil. Hal-hal baik tentang menggunakan imbal hasil adalah bahwa ini adalah cara yang sangat cepat untuk menerapkan pola iterator, sehingga semuanya dievaluasi dengan malas.
Mendelt
111
Juga patut dicatat, Anda dapat menggunakan yield break;ketika Anda tidak ingin mengembalikan barang lagi.
Rory
7
yieldbukan kata kunci. Jika demikian maka saya tidak dapat menggunakan hasil sebagai pengidentifikasi seperti dalamint yield = 500;
Brandin
5
@Brandin itu karena semua bahasa pemrograman mendukung dua jenis kata kunci yaitu dilindungi undang-undang dan kontekstual. hasil jatuh dalam kategori selanjutnya yang mengapa kode Anda tidak dilarang oleh kompiler C #. Lebih detail di sini: ericlippert.com/2009/05/11/reserved-and-contextual-keywords Anda akan senang mengetahui bahwa ada juga kata-kata khusus yang tidak dikenali sebagai kata kunci oleh suatu bahasa. Untuk mis goto di java. Lebih detail di sini: stackoverflow.com/questions/2545103/…
RBT
7
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'. tidak terdengar benar bagiku. Saya selalu memikirkan kata kunci c # yield dalam konteks "hasil panen panen berlimpah", bukannya "hasil mobil ke pejalan kaki".
Zack
369

Pengulangan. Ini menciptakan mesin negara "di bawah penutup" yang mengingat di mana Anda berada pada setiap siklus fungsi tambahan dan mengambil dari sana.

Joel Coehoorn
sumber
210

Yield memiliki dua kegunaan besar,

  1. Ini membantu untuk menyediakan iterasi khusus tanpa membuat koleksi temp.

  2. Ini membantu untuk melakukan iterasi stateful. masukkan deskripsi gambar di sini

Untuk menjelaskan dua poin di atas secara lebih demonstratif, saya telah membuat video sederhana yang dapat Anda tonton di sini

Shivprasad Koirala
sumber
13
Video membantu saya untuk memahami dengan jelas yield. @ Artikel proyek kode kode ShivprasadKoirala Apa gunanya C # Yield? dari penjelasan yang sama juga merupakan sumber yang bagus
Dush
Saya juga akan menambahkan sebagai poin ketiga yaitu yield"cepat" cara untuk membuat IEnumerator kustom (bukan kelas yang mengimplementasikan antarmuka IEnumerator).
MrTourkos
Saya menonton video Anda Shivprasad dan menjelaskan dengan jelas penggunaan kata kunci hasil.
Tore Aurstad
Terima kasih untuk videonya !. Dijelaskan dengan sangat baik!
Roblogic
Video yang bagus, tetapi bertanya-tanya ... Implementasi menggunakan yield jelas lebih bersih, tetapi pada dasarnya harus membuat memori temp sendiri atau / dan Daftar secara internal untuk melacak keadaan (atau lebih tepatnya membuat mesin negara). Jadi, apakah "Yield" melakukan hal lain selain membuat implementasi lebih sederhana dan membuat segalanya terlihat lebih baik atau ada sesuatu yang lain untuk itu? Bagaimana dengan efisiensi, apakah menjalankan kode menggunakan Yield lebih atau kurang efisien / cepat daripada tanpa?
toughQuestions
135

Baru-baru ini Raymond Chen juga menjalankan serangkaian artikel menarik tentang kata kunci hasil.

Meskipun secara nominal digunakan untuk dengan mudah menerapkan pola iterator, tetapi dapat digeneralisasi menjadi mesin negara. Tidak ada gunanya mengutip Raymond, bagian terakhir juga menghubungkan ke penggunaan lain (tetapi contoh di blog Entin sangat bagus, menunjukkan cara menulis kode aman async).

Svend
sumber
Ini harus dipilih. Manis bagaimana dia menjelaskan tujuan dari operator dan internal.
sajidnizami
3
bagian 1 menjelaskan gula sintaksis dari "imbal hasil". menjelaskan dengan sangat baik!
Dror Weiss
99

Pada pandangan pertama, imbal hasil adalah gula NET untuk mengembalikan IEnumerable .

Tanpa hasil, semua item koleksi dibuat sekaligus:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        return new List<SomeData> {
            new SomeData(), 
            new SomeData(), 
            new SomeData()
        };
    }
}

Kode yang sama menggunakan hasil, itu mengembalikan item demi item:

class SomeData
{
    public SomeData() { }

    static public IEnumerable<SomeData> CreateSomeDatas()
    {
        yield return new SomeData();
        yield return new SomeData();
        yield return new SomeData();
    }
}

Keuntungan menggunakan hasil adalah bahwa jika fungsi yang memakan data Anda hanya membutuhkan item pertama dari koleksi, sisa item tidak akan dibuat.

Operator hasil memungkinkan pembuatan item seperti yang diminta. Itu alasan yang bagus untuk menggunakannya.

Marquinho Peli
sumber
40

yield returndigunakan dengan enumerator. Pada setiap panggilan pernyataan hasil, kontrol dikembalikan ke penelepon tetapi memastikan bahwa kondisi callee dipertahankan. Karena itu, ketika penelepon menyebutkan elemen berikutnya, ia melanjutkan eksekusi dalam metode callee dari pernyataan segera setelah yieldpernyataan.

Mari kita coba pahami ini dengan sebuah contoh. Dalam contoh ini, sesuai dengan setiap baris yang saya sebutkan urutan di mana eksekusi mengalir.

static void Main(string[] args)
{
    foreach (int fib in Fibs(6))//1, 5
    {
        Console.WriteLine(fib + " ");//4, 10
    }            
}

static IEnumerable<int> Fibs(int fibCount)
{
    for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
    {
        yield return prevFib;//3, 9
        int newFib = prevFib + currFib;//6
        prevFib = currFib;//7
        currFib = newFib;//8
    }
}

Juga, status dipertahankan untuk setiap enumerasi. Misalkan, saya memiliki panggilan lain ke Fibs()metode maka negara akan diatur ulang untuk itu.

RKS
sumber
2
set prevFib = 1 - angka Fibonacci pertama adalah "1", bukan "0"
fubo
31

Secara intuitif, kata kunci mengembalikan nilai dari fungsi tanpa meninggalkannya, yaitu dalam contoh kode Anda, mengembalikan nilai saat ini itemdan kemudian melanjutkan loop. Lebih formal, ini digunakan oleh kompiler untuk menghasilkan kode untuk iterator . Iterator adalah fungsi yang mengembalikan IEnumerableobjek. The MSDN memiliki beberapa artikel tentang mereka.

Konrad Rudolph
sumber
4
Yah, tepatnya itu tidak melanjutkan loop, itu berhenti sampai orangtua memanggil "iterator.next ()".
Alex
8
@bitbit Itulah sebabnya saya menggunakan "secara intuitif" dan "lebih formal".
Konrad Rudolph
31

Daftar atau implementasi array memuat semua item segera sedangkan implementasi hasil menyediakan solusi eksekusi yang ditangguhkan.

Dalam praktiknya, seringkali diinginkan untuk melakukan jumlah pekerjaan minimum yang diperlukan untuk mengurangi konsumsi sumber daya suatu aplikasi.

Misalnya, kami mungkin memiliki aplikasi yang memproses jutaan catatan dari basis data. Manfaat berikut dapat dicapai ketika kami menggunakan IEnumerable dalam model berbasis tarik eksekusi yang ditangguhkan:

  • Skalabilitas, keandalan, dan prediktabilitas cenderung meningkat karena jumlah catatan tidak secara signifikan memengaruhi kebutuhan sumber daya aplikasi.
  • Kinerja dan daya tanggap cenderung meningkat karena pemrosesan dapat segera dimulai alih-alih menunggu seluruh koleksi dimuat terlebih dahulu.
  • Pemulihan dan pemanfaatan kemungkinan akan meningkat karena aplikasi dapat dihentikan, dimulai, terganggu atau gagal. Hanya item yang sedang berjalan yang akan hilang dibandingkan dengan pengambilan sebelumnya semua data yang hanya menggunakan sebagian dari hasil yang benar-benar digunakan.
  • Pemrosesan berkelanjutan dimungkinkan di lingkungan di mana aliran beban kerja yang konstan ditambahkan.

Berikut adalah perbandingan antara membangun koleksi terlebih dahulu seperti daftar dibandingkan dengan menggunakan hasil.

Daftar Contoh

    public class ContactListStore : IStore<ContactModel>
    {
        public IEnumerable<ContactModel> GetEnumerator()
        {
            var contacts = new List<ContactModel>();
            Console.WriteLine("ContactListStore: Creating contact 1");
            contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
            Console.WriteLine("ContactListStore: Creating contact 2");
            contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
            Console.WriteLine("ContactListStore: Creating contact 3");
            contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
            return contacts;
        }
    }

    static void Main(string[] args)
    {
        var store = new ContactListStore();
        var contacts = store.GetEnumerator();

        Console.WriteLine("Ready to iterate through the collection.");
        Console.ReadLine();
    }

Output Output
ContactListStore: Membuat kontak 1
ContactListStore: Membuat kontak 2
ContactListStore: Membuat kontak 3
Siap untuk beralih melalui koleksi.

Catatan: Seluruh koleksi dimuat ke dalam memori tanpa meminta satu pun item dalam daftar

Contoh Hasil

public class ContactYieldStore : IStore<ContactModel>
{
    public IEnumerable<ContactModel> GetEnumerator()
    {
        Console.WriteLine("ContactYieldStore: Creating contact 1");
        yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
        Console.WriteLine("ContactYieldStore: Creating contact 2");
        yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
        Console.WriteLine("ContactYieldStore: Creating contact 3");
        yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
    }
}

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();

    Console.WriteLine("Ready to iterate through the collection.");
    Console.ReadLine();
}

Output Konsol
Siap untuk beralih melalui koleksi.

Catatan: Koleksi tidak dieksekusi sama sekali. Ini karena sifat "eksekusi yang ditangguhkan" dari IEnumerable. Membangun item hanya akan terjadi ketika itu benar-benar diperlukan.

Mari panggil koleksi lagi dan pantau perilaku ketika kami mengambil kontak pertama dalam koleksi.

static void Main(string[] args)
{
    var store = new ContactYieldStore();
    var contacts = store.GetEnumerator();
    Console.WriteLine("Ready to iterate through the collection");
    Console.WriteLine("Hello {0}", contacts.First().FirstName);
    Console.ReadLine();
}

Output Konsol
Siap untuk beralih melalui koleksi
ContactYieldStore: Membuat kontak 1
Halo Bob

Bagus! Hanya kontak pertama yang dibangun ketika klien "menarik" item keluar dari koleksi.

Strydom
sumber
1
Jawaban ini perlu lebih diperhatikan! Thx
leon22
@ leon22 benar-benar 2
SNR
26

Berikut ini adalah cara sederhana untuk memahami konsep: Gagasan dasarnya adalah, jika Anda menginginkan koleksi yang dapat Anda gunakan " foreach", tetapi mengumpulkan barang-barang ke dalam koleksi itu mahal karena beberapa alasan (seperti meminta mereka keluar dari database), DAN Anda sering kali tidak perlu seluruh koleksi, maka Anda membuat fungsi yang membangun koleksi satu item pada suatu waktu dan mengembalikannya ke konsumen (yang kemudian dapat menghentikan upaya pengumpulan awal).

Pikirkan seperti ini: Anda pergi ke meja daging dan ingin membeli satu pon ham yang diiris. Tukang daging mengambil ham 10 pon ke belakang, meletakkannya di mesin pengiris, mengiris semuanya, lalu membawa tumpukan irisan kembali ke Anda dan mengukur satu pon itu. (LAMA). Dengan yield, tukang daging membawa mesin pengiris ke meja, dan mulai mengiris dan "menghasilkan" masing-masing irisan ke skala sampai ukuran 1 pon, kemudian membungkusnya untuk Anda dan Anda selesai. Jalan Lama mungkin lebih baik bagi tukang daging (memungkinkannya mengatur mesinnya sesuai keinginannya), tetapi Jalan Baru jelas lebih efisien dalam banyak kasus bagi konsumen.

kmote
sumber
18

Kata yieldkunci memungkinkan Anda untuk membuat IEnumerable<T>formulir di blok iterator . Blok iterator ini mendukung eksekusi yang ditangguhkan dan jika Anda tidak terbiasa dengan konsep itu mungkin tampak hampir ajaib. Namun, pada akhirnya hanya kode yang dijalankan tanpa trik aneh.

Blok iterator dapat digambarkan sebagai gula sintaksis di mana kompiler menghasilkan mesin negara yang melacak sejauh mana enumerasi enumerable telah berkembang. Untuk menghitung enumerable, Anda sering menggunakan foreachloop. Namun, satu foreachlingkaran juga merupakan gula sintaksis. Jadi, Anda adalah dua abstraksi yang dihapus dari kode asli yang mengapa awalnya mungkin sulit untuk memahami bagaimana semuanya bekerja bersama.

Asumsikan bahwa Anda memiliki blok iterator yang sangat sederhana:

IEnumerable<int> IteratorBlock()
{
    Console.WriteLine("Begin");
    yield return 1;
    Console.WriteLine("After 1");
    yield return 2;
    Console.WriteLine("After 2");
    yield return 42;
    Console.WriteLine("End");
}

Blok iterator nyata sering memiliki kondisi dan loop tetapi ketika Anda memeriksa kondisi dan membuka gulungannya, mereka masih berakhir sebagai yieldpernyataan yang disatukan dengan kode lain.

Untuk menghitung blok iterator, sebuah foreachloop digunakan:

foreach (var i in IteratorBlock())
    Console.WriteLine(i);

Berikut ini hasilnya (tidak ada kejutan di sini):

Mulai
1
Setelah 1
2
Setelah 2
42
Akhir

Sebagaimana dinyatakan di atas foreachadalah gula sintaksis:

IEnumerator<int> enumerator = null;
try
{
    enumerator = IteratorBlock().GetEnumerator();
    while (enumerator.MoveNext())
    {
        var i = enumerator.Current;
        Console.WriteLine(i);
    }
}
finally
{
    enumerator?.Dispose();
}

Dalam upaya untuk mengurai ini saya telah membuat diagram urutan dengan abstraksi dihapus:

Diagram urutan blok C # iterator

Mesin negara yang dihasilkan oleh kompiler juga mengimplementasikan enumerator tetapi untuk membuat diagram lebih jelas, saya telah menunjukkannya sebagai instance yang terpisah. (Ketika mesin negara disebutkan dari utas lain, Anda benar-benar mendapatkan instance terpisah tetapi detail itu tidak penting di sini.)

Setiap kali Anda menyebut blok iterator Anda, instance baru dari mesin state dibuat. Namun, tidak ada kode Anda di blok iterator yang dieksekusi sampai enumerator.MoveNext()dijalankan untuk pertama kalinya. Inilah cara kerja eksekusi yang ditangguhkan. Berikut adalah contoh (agak konyol):

var evenNumbers = IteratorBlock().Where(i => i%2 == 0);

Pada titik ini iterator belum dieksekusi. The Whereklausul menciptakan baru IEnumerable<T>yang membungkus yang IEnumerable<T>dikembalikan oleh IteratorBlocktetapi enumerable ini belum disebutkan. Ini terjadi ketika Anda menjalankan foreachloop:

foreach (var evenNumber in evenNumbers)
    Console.WriteLine(eventNumber);

Jika Anda menghitung enumerable dua kali maka instance baru dari mesin state dibuat setiap kali dan blok iterator Anda akan mengeksekusi kode yang sama dua kali.

Perhatikan bahwa metode LINQ suka ToList(), ToArray(), First(), Count()dll akan menggunakan foreachloop untuk menghitung enumerable tersebut. Misalnya ToList()akan menghitung semua elemen yang dapat dihitung dan menyimpannya dalam daftar. Anda sekarang dapat mengakses daftar untuk mendapatkan semua elemen enumerable tanpa blok iterator mengeksekusi lagi. Ada trade-off antara menggunakan CPU untuk menghasilkan elemen-elemen enumerable berulang kali dan memori untuk menyimpan elemen-elemen enumerasi untuk mengaksesnya berkali-kali saat menggunakan metode seperti ToList().

Martin Liversage
sumber
18

Jika saya memahami ini dengan benar, inilah cara saya akan mengatakan ini dari perspektif fungsi mengimplementasikan IEnumerable dengan hasil.

  • Ini dia.
  • Telepon lagi jika Anda membutuhkan yang lain.
  • Saya akan mengingat apa yang sudah saya berikan kepada Anda.
  • Saya hanya akan tahu jika saya bisa memberi Anda yang lain ketika Anda menelepon lagi.
Casey Plummer
sumber
sederhana & cemerlang
Harry
10

Kata kunci C # yield, sederhananya, memungkinkan banyak panggilan ke badan kode, yang disebut sebagai iterator, yang tahu cara mengembalikan sebelum dilakukan dan, ketika dipanggil lagi, melanjutkan dari mana ia tinggalkan - artinya membantu iterator menjadi transparan secara transparan per setiap item dalam urutan yang dikembalikan oleh iterator dalam panggilan berurutan.

Dalam JavaScript, konsep yang sama disebut Generator.

BoiseBaked
sumber
Penjelasan terbaik. Apakah ini juga generator yang sama dengan python?
petrosmm
7

Ini adalah cara yang sangat sederhana dan mudah untuk membuat enumerable untuk objek Anda. Kompiler membuat kelas yang membungkus metode Anda dan yang mengimplementasikan, dalam hal ini, IEnumerable <object>. Tanpa kata kunci hasil, Anda harus membuat objek yang mengimplementasikan IEnumerable <object>.


sumber
5

Itu menghasilkan urutan enumerable. Apa yang dilakukannya sebenarnya menciptakan urutan IEnumerable lokal dan mengembalikannya sebagai hasil metode

aku
sumber
3

Tautan ini memiliki contoh sederhana

Contoh-contoh yang lebih sederhana ada di sini

public static IEnumerable<int> testYieldb()
{
    for(int i=0;i<3;i++) yield return 4;
}

Perhatikan bahwa pengembalian hasil tidak akan kembali dari metode. Anda bahkan dapat menempatkan WriteLinesetelahyield return

Di atas menghasilkan IEnumerable dari 4 int 4,4,4,4

Sini dengan WriteLine. Akan menambahkan 4 ke daftar, mencetak abc, lalu menambahkan 4 ke daftar, lalu menyelesaikan metode dan benar-benar kembali dari metode (setelah metode selesai, seperti yang akan terjadi dengan prosedur tanpa pengembalian). Tapi ini akan memiliki nilai, IEnumerabledaftar ints, yang dikembalikan setelah selesai.

public static IEnumerable<int> testYieldb()
{
    yield return 4;
    console.WriteLine("abc");
    yield return 4;
}

Perhatikan juga bahwa ketika Anda menggunakan hasil, apa yang Anda kembalikan bukan dari jenis yang sama dengan fungsi. Itu dari jenis elemen dalam IEnumerabledaftar.

Anda menggunakan hasil dengan tipe pengembalian metode sebagai IEnumerable. Jika tipe pengembalian metode adalah intatau List<int>dan Anda gunakan yield, maka itu tidak akan dikompilasi. Anda dapat menggunakan IEnumerablejenis metode pengembalian tanpa hasil tetapi tampaknya Anda mungkin tidak dapat menggunakan hasil tanpa IEnumerablejenis metode kembali.

Dan untuk mengeksekusinya Anda harus menyebutnya dengan cara khusus.

static void Main(string[] args)
{
    testA();
    Console.Write("try again. the above won't execute any of the function!\n");

    foreach (var x in testA()) { }


    Console.ReadLine();
}



// static List<int> testA()
static IEnumerable<int> testA()
{
    Console.WriteLine("asdfa");
    yield return 1;
    Console.WriteLine("asdf");
}
barlop
sumber
perhatikan - jika mencoba memahami SelectMany, ia menggunakan hasil dan juga obat generik .. contoh ini dapat membantu public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }dan public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
barlop
Sepertinya penjelasan yang sangat bagus! Ini bisa menjadi jawaban yang diterima.
pongapundit
@pongapundit terima kasih, jawaban saya jelas dan sederhana, tapi saya sendiri belum menggunakan hasil, penjawab lain memiliki lebih banyak pengalaman dengan itu dan pengetahuan tentang penggunaannya daripada yang saya lakukan. Apa yang saya tulis tentang hasil di sini mungkin dari menggaruk-garuk kepala saya mencoba mencari tahu beberapa jawaban di sini dan pada tautan dotnetperls itu! Tetapi karena saya tidak tahu yield returnitu dengan baik (selain hal sederhana yang saya sebutkan), dan belum banyak menggunakannya dan tidak tahu banyak tentang penggunaannya, saya tidak berpikir ini yang seharusnya diterima.
barlop
3

Satu poin utama tentang kata kunci hasil adalah Eksekusi Malas . Sekarang yang saya maksud dengan Lazy Execution adalah mengeksekusi ketika dibutuhkan. Cara yang lebih baik untuk menjelaskannya adalah dengan memberi contoh

Contoh: Tidak menggunakan Yield yaitu Eksekusi Tanpa Malas.

        public static IEnumerable<int> CreateCollectionWithList()
        {
            var list =  new List<int>();
            list.Add(10);
            list.Add(0);
            list.Add(1);
            list.Add(2);
            list.Add(20);

            return list;
        }

Contoh: menggunakan Yield yaitu Eksekusi Malas.

    public static IEnumerable<int> CreateCollectionWithYield()
    {
        yield return 10;
        for (int i = 0; i < 3; i++) 
        {
            yield return i;
        }

        yield return 20;
    }

Sekarang ketika saya memanggil kedua metode.

var listItems = CreateCollectionWithList();
var yieldedItems = CreateCollectionWithYield();

Anda akan melihat listItems akan memiliki 5 item di dalamnya (arahkan mouse Anda ke listItems saat debugging). Sedangkan yieldItems hanya akan memiliki referensi ke metode dan bukan item. Itu berarti belum menjalankan proses mendapatkan item di dalam metode. Cara yang sangat efisien untuk mendapatkan data hanya saat dibutuhkan. Implementasi hasil aktual dapat dilihat di ORM seperti Entity Framework dan NHibernate dll.

maxspan
sumber
-3

Itu mencoba untuk membawa beberapa Kebaikan Ruby :)
Konsep: Ini adalah beberapa contoh Kode Ruby yang mencetak setiap elemen array

 rubyArray = [1,2,3,4,5,6,7,8,9,10]
    rubyArray.each{|x| 
        puts x   # do whatever with x
    }

Setiap implementasi metode Array menghasilkan kontrol ke pemanggil ('menempatkan x') dengan masing - masing elemen array yang disajikan dengan rapi sebagai x. Penelepon kemudian dapat melakukan apa pun yang perlu dilakukan dengan x.

Namun. Net tidak pergi jauh-jauh ke sini .. C # tampaknya telah digabungkan hasil dengan IEnumerable, dengan cara memaksa Anda untuk menulis loop foreach di pemanggil seperti yang terlihat dalam respons Mendelt. Agak kurang elegan.

//calling code
foreach(int i in obCustomClass.Each())
{
    Console.WriteLine(i.ToString());
}

// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
   for(int iLooper=0; iLooper<data.Length; ++iLooper)
        yield return data[iLooper]; 
}
Gishu
sumber
7
-1 Jawaban ini kedengarannya tidak benar bagi saya. Ya, C # yielddigabungkan dengan IEnumerable, dan C # tidak memiliki konsep Ruby dari "blok". Tetapi C # memiliki lambdas, yang dapat memungkinkan implementasi suatu ForEachmetode, sama seperti Ruby each. Namun ini tidak berarti akan menjadi ide yang baik untuk melakukannya .
rsenna
Lebih baik lagi: public IEnumerable <int> Each () {int index = 0; menghasilkan data pengembalian [indeks ++]; }
ata