C # 'da kullanılan verim anahtar kelimesi nedir?


828

In Nasıl Can I IList <Yalnızca Fragment Açığa> cevapların soruyu bir aşağıdaki kod parçacığını vardı:

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

Ürün anahtar kelimesi orada ne yapar? Birkaç yerde ve başka bir soruya atıfta bulunduğunu gördüm, ancak aslında ne yaptığını tam olarak anlayamadım. Ben bir iplik diğerine veren anlamında verimi düşünmeye alışkınım, ama bu burada alakalı görünmüyor.


Bu konuda sadece MSDN bağlantısı burada msdn.microsoft.com/en-us/library/vstudio/9k7k7cf0.aspx
Geliştirici

14
Bu şaşırtıcı değil. Karışıklık, "geri dönüşü" fonksiyon çıktısı olarak görmeye şartlandığımızdan gelirken, öncesinde "verim" gelmez.
Larry

4
Dokümanları okudum ama korkuyorum hala anlamadım :(
Ortund

Yanıtlar:


737

yieldAnahtar kelime aslında burada oldukça fazla yok.

İşlev, IEnumerable<object>arabirimi uygulayan bir nesne döndürür . Bir çağrı fonksiyonu foreachbu nesne üzerinde başlarsa , fonksiyon "verim" olana kadar tekrar çağrılır. Bu, C # 2.0'da sunulan sözdizimsel şekerdir . Önceki sürümlerde böyle şeyler yapmak için kendi nesnelerinizi IEnumerableve IEnumeratornesnelerinizi oluşturmanız gerekiyordu .

Bunun gibi kodu anlamanın en kolay yolu bir örnek yazmak, bazı kesme noktaları ayarlamak ve ne olduğunu görmektir. Bu örneğe adım atmayı deneyin:

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;
}

Örneği incelediğinizde, ilk Integers()iade çağrısını bulacaksınız 1. İkinci çağrı geri döner 2ve hat yield return 1tekrar yürütülmez.

İşte gerçek hayattan bir örnek:

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);
                }
            }
        }
    }
}

113
Bu daha kolay olurdu, ben sadece burada tamsayı kullanarak getiri getiri nasıl çalışır göstermek için kullanıyorum. Verim getirisini kullanma ile ilgili güzel şeyler, yineleyici desenini uygulamanın çok hızlı bir yolu olduğundan, işler tembel olarak değerlendirilir.
Mendelt

111
Ayrıca, yield break;daha fazla ürün iade etmek istemediğinizde kullanabileceğinizi belirtmek gerekir .
Rory

7
yieldbir anahtar kelime değil. Eğer öyleyse verimi olduğu gibi tanımlayıcı olarak kullanamazdımint yield = 500;
Brandin

5
@Brandin, çünkü tüm programlama dilleri ayrılmış ve bağlamsal olarak iki tür anahtar kelimeyi destekliyor. verim sonraki kategoriye düşer, bu yüzden kodunuz C # derleyicisi tarafından yasaklanmamıştır. Daha fazla ayrıntı burada: ericlippert.com/2009/05/11/reserved-and-contextual-keywords Bir dil tarafından anahtar kelime olarak tanınmayan ayrılmış kelimeler olduğunu bilmek sizi heyecanlandırır. Örneğin java'da git. Daha fazla ayrıntı için: stackoverflow.com/questions/2545103/…
RBT

7
'If a calling function starts foreach-ing over this object the function is called again until it "yields"'. bana doğru gelmiyor. Ben her zaman "araba yaya verir" yerine, "ürün bol bir hasat verir" bağlamında c # verim anahtar kelime düşündüm.
Zack

369

Yineleme. "Kapakların altında", fonksiyonun her bir ek döngüsünde nerede olduğunuzu hatırlayan ve oradan alan bir durum makinesi oluşturur.


210

Getiri iki harika kullanıma sahiptir,

  1. Geçici koleksiyonlar oluşturmadan özel yineleme sağlamaya yardımcı olur.

  2. Durumsal yineleme yapmaya yardımcı olur. resim açıklamasını buraya girin

Yukarıdaki iki noktayı daha açıklamak için burada izleyebileceğiniz basit bir video hazırladım


13
Video bana açıkça anlamamı sağlıyor yield. @ ShivprasadKoirala'nın kod projesi makalesi C # Getiri ne işe yarar ? aynı açıklama iyi bir kaynaktır
Dush

Ayrıca yieldözel bir IEnumerator (bunun yerine bir sınıf IEnumerator arabirimini uygulamak zorunda) oluşturmak için "hızlı" yolu olan üçüncü bir nokta olarak eklemek istiyorum .
MrTourkos

Videonuzu Shivprasad izledim ve verim anahtar kelimesinin kullanımını açıkça açıkladı.
Tore Aurstad

Video için teşekkürler !. Çok iyi açıkladı!
Roblogic

Harika video, ama merak ediyorum ... Verimi kullanan uygulama açıkça daha temizdir, ancak durumu takip etmek (veya bir durum makinesi oluşturmak) için esasen kendi geçici belleğini veya / ve Listesini oluşturmak olmalıdır. Peki, "Getiri" uygulamayı daha basit hale getirmek ve işleri daha iyi göstermek için başka bir şey yapıyor mu ya da başka bir şey var mı? Verimliliğe ne dersiniz?
toughQuestions

135

Son zamanlarda Raymond Chen, anahtar kelime ile ilgili ilginç bir dizi makale yayınladı.

Nominal olarak bir yineleyici modelini kolayca uygulamak için kullanılır, ancak bir durum makinesinde genelleştirilebilir. Raymond'u alıntılamanın bir anlamı yok, son bölüm diğer kullanımlarla da bağlantılıdır (ancak Entin'in blogundaki örnek esp iyi kod olup, zaman uyumsuz güvenli kodun nasıl yazılacağını gösterir).


Bunun oylanması gerekiyor. Operatörün ve içselin amacını nasıl açıkladığını tatlı.
sajidnizami

3
bölüm 1 "verim geri dönüşü" nin sözdizimsel şekerini açıklar. mükemmel açıklayan!
Dror Weiss

99

İlk bakışta, getiri getirisi bir IEnumerable'ı döndürmek için bir .NET şekeri .

Verim olmadan, koleksiyonun tüm öğeleri bir kerede oluşturulur:

class SomeData
{
    public SomeData() { }

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

Verimi kullanarak aynı kod, öğeyi öğeye göre döndürür:

class SomeData
{
    public SomeData() { }

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

Verimi kullanmanın avantajı, verilerinizi tüketen işlevin yalnızca koleksiyonun ilk öğesine ihtiyacı olması durumunda, geri kalan öğelerin oluşturulmayacağıdır.

Verim operatörü, talep edildiği gibi öğelerin oluşturulmasına izin verir. Bunu kullanmak için iyi bir sebep.


40

yield returnsayıcılar ile kullanılır. Her bir verim çağrısı ifadesinde, kontrol arayana geri gönderilir ancak arayanın durumunun korunmasını sağlar. Bu nedenle, arayan bir sonraki öğeyi numaralandırdığında, ifadeden hemen sonra ifadeden callee yönteminde yürütülmeye devam eder yield.

Bunu bir örnekle anlamaya çalışalım. Bu örnekte, her satıra karşılık gelen yürütme akışından bahsetmiştim.

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
    }
}

Ayrıca, her numaralandırma için devlet korunur. Varsayalım, Fibs()yöntem için başka bir çağrı var, sonra durum bunun için sıfırlanacak.


2
set prevFib = 1 - ilk Fibonacci numarası "1", "0" değil
fubo

31

Sezgisel olarak, anahtar kelime işlevden ayrılmadan bir değer döndürür, yani kod örneğinizde geçerli itemdeğeri döndürür ve sonra döngüyü sürdürür. Daha resmi olarak, derleyici tarafından bir yineleyici için kod oluşturmak için kullanılır . Yineleyiciler, IEnumerablenesneleri döndüren işlevlerdir . MSDN birkaç vardır makaleler onlar hakkında.


4
Kesin olarak, döngüyü devam ettirmez, üst öğe "iterator.next ()" çağrıncaya kadar onu duraklatır.
Alex

8
@jitbit Bu yüzden “sezgisel” ve “daha ​​resmi” kullandım.
Konrad Rudolph

31

Bir liste veya dizi uygulaması tüm öğeleri hemen yüklerken, verim uygulaması ertelenmiş bir yürütme çözümü sağlar.

Uygulamada, bir uygulamanın kaynak tüketimini azaltmak için gereken minimum iş miktarını gerçekleştirmek genellikle istenir.

Örneğin, bir veritabanından milyonlarca kaydı işleyen bir uygulamamız olabilir. Ertelenmiş bir yürütme çekme tabanlı modelde IEnumerable kullandığımızda aşağıdaki yararlar elde edilebilir:

  • Kayıt sayısı, uygulamanın kaynak gereksinimlerini önemli ölçüde etkilemediğinden ölçeklenebilirlik, güvenilirlik ve öngörülebilirlik artacaktır .
  • Performansın ve yanıt verme hızının artması muhtemeldir, çünkü işlem tüm koleksiyonun önce yüklenmesini beklemek yerine hemen başlayabilir.
  • Uygulama durdurulabileceği, başlatılabileceği, kesilebileceği veya başarısız olabileceğinden kurtarılabilirlik ve kullanımın iyileşme olasılığı yüksektir. Yalnızca devam eden öğeler, sonuçların yalnızca bir bölümünün kullanılmasının kullanıldığı tüm verilerin önceden getirilmesine kıyasla kaybolacaktır.
  • Sabit iş yükü akışlarının eklendiği ortamlarda sürekli işleme mümkündür.

İşte verimi kullanmaya kıyasla bir liste gibi bir koleksiyon oluşturmak arasında bir karşılaştırma.

Liste Örneği

    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();
    }

Konsol Çıkışı
ContactListStore: Kişi oluşturma 1
ContactListStore: Kişi oluşturma 2
ContactListStore: Kişi oluşturma 3
Koleksiyon boyunca yinelemeye hazır.

Not: Tüm koleksiyon, listede tek bir öğe bile sormadan belleğe yüklendi

Verim Örneği

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();
}

Konsol Çıkışı
Koleksiyon boyunca yinelemeye hazır.

Not: Koleksiyon hiç yürütülmedi. Bu, IEnumerable'ın "ertelenmiş yürütme" doğasından kaynaklanmaktadır. Bir öğenin oluşturulması yalnızca gerçekten gerekli olduğunda gerçekleşir.

Koleksiyonu tekrar arayalım ve koleksiyondaki ilk kişiyi aldığımızda davranışı engelleyelim.

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();
}

Konsol Çıkışı
Koleksiyon üzerinden yinelemeye hazır
ContactYieldStore: Kişi oluşturma 1
Hello Bob

Güzel! İstemci öğeyi koleksiyondan "çıkardığında" yalnızca ilk kişi oluşturuldu.


1
Bu cevabın daha fazla ilgiye ihtiyacı var! Thx
leon22

@ leon22 kesinlikle +2
snr

26

İşte konsepti anlamanın basit bir yolu: Temel fikir, " foreach" kullanabileceğiniz bir koleksiyon istiyorsanız , ancak öğeleri koleksiyona toplamak bazı nedenlerden dolayı pahalıdır (bunları bir veritabanından sorgulamak gibi), VE genellikle tüm koleksiyona ihtiyacınız olmayacak, daha sonra koleksiyonu bir kerede bir öğe oluşturan ve tüketiciye geri veren bir işlev yaratırsınız (bu da toplama çabasını erken sonlandırabilir).

Şöyle düşünün: Et tezgahına gidin ve bir kilo dilimlenmiş jambon satın almak istiyorsunuz. Kasap, arkaya 10 kiloluk bir jambon alır, dilimleme makinesine koyar, her şeyi dilimler, daha sonra dilimlerin yığınını size geri getirir ve bir kilogramını ölçer. (Eski yol). İle yield, kasap tezgaha dilimleyici makine getiriyor ve 1-kiloluk ölçen kadar ölçek üzerine dilimleme ve "verimli" her dilimi, sonra başlar sizin için sarar ve bitirdiniz. Eski Yol kasap için daha iyi olabilir (makinelerini istediği gibi organize etmesine izin verir), ancak Yeni Yol çoğu durumda tüketici için açıkça daha etkilidir.


18

yieldAnahtar kelime bir oluşturmanıza olanak sağlar IEnumerable<T>bir formdan yineleyici bloğu . Bu yineleyici bloğu ertelenmiş yürütmeyi destekler ve kavramı bilmiyorsanız neredeyse büyülü görünebilir. Ancak, günün sonunda garip hileler olmadan çalışan sadece kod.

Bir yineleyici blok, derleyicinin numaralandırmanın numaralandırılmasının ne kadar ilerlediğini takip eden bir durum makinesi ürettiği sözdizimsel şeker olarak tanımlanabilir. Bir numaralandırmayı numaralandırmak için genellikle bir foreachdöngü kullanırsınız . Bununla birlikte, bir foreachdöngü aynı zamanda sözdizimsel şekerdir. Yani gerçek koddan çıkarılmış iki soyutlamasınız, bu yüzden başlangıçta hepsinin birlikte nasıl çalıştığını anlamak zor olabilir.

Çok basit bir yineleyici bloğunuz olduğunu varsayın:

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");
}

Gerçek yineleyici blokları genellikle koşullara ve döngülere sahiptir, ancak koşulları kontrol ettiğinizde ve döngüleri kaldırdığınızda yield, diğer kodlarla araya eklenmiş ifadeler olarak son bulurlar .

Yineleyici bloğunu numaralandırmak için bir foreachdöngü kullanılır:

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

İşte çıktı (burada sürpriz yok):

Başla
1
1'den sonra
2
2 Sonrası
42
Son

Yukarıda belirtildiği gibi foreachsözdizimsel şeker:

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

Bu çözmek için bir soyutlama kaldırıldı ile bir dizi diyagramı sandık:

C # yineleyici blok dizisi diyagramı

Derleyici tarafından üretilen durum makinesi de numaralandırıcıyı uygular, ancak şemayı daha açık hale getirmek için bunları ayrı örnekler olarak gösterdim. (Durum makinesi başka bir iş parçacığından numaralandırıldığında, aslında ayrı örnekler alırsınız, ancak bu ayrıntı burada önemli değildir.)

Yineleyicinizi her çağırdığınızda durum makinesinin yeni bir örneği oluşturulur. Ancak, yineleyici bloğundaki kodlarınızın hiçbiri enumerator.MoveNext()ilk kez yürütülene kadar yürütülmez. Ertelenmiş yürütme böyle çalışır. İşte (oldukça aptalca) bir örnek:

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

Bu noktada yineleyici yürütülmemiştir. WhereFıkra yeni oluşturur IEnumerable<T>sarar o IEnumerable<T>tarafından döndürülen IteratorBlockancak bu enumerable sayılan henüz bulunmuyor. Bir foreachdöngü yürüttüğünüzde bu gerçekleşir :

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

Numaralandırmayı iki kez numaralandırırsanız, her seferinde durum makinesinin yeni bir örneği oluşturulur ve yineleyici bloğunuz aynı kodu iki kez yürütür.

LINQ yöntemleri gibi Bildirimi olduğunu ToList(), ToArray(), First(), Count()vb kullanacak foreachenumerator numaralandırma döngü. Örneğin ToList(), numaralandırılabilir öğenin tüm öğelerini numaralandıracak ve bir listede saklayacaktır. Artık yineleyicinin tüm öğelerini yineleyici bloğu tekrar yürütmeden almak için listeye erişebilirsiniz. Numaralandırılabilir öğelerin öğelerini üretmek için CPU kullanımı ile numaralandırma öğelerini depolamak gibi bellekler gibi yöntemleri kullanırken bunlara birden çok kez erişmek arasında bir denge vardır ToList().


18

Bunu doğru bir şekilde anlarsam, bunu IEnumerable uygulayan fonksiyon açısından verimle nasıl ifade edeceğim.

  • Işte bir tane.
  • Başka birine ihtiyacınız varsa tekrar arayın.
  • Sana ne verdiğimi hatırlayacağım.
  • Sadece tekrar aradığın zaman sana bir başkasını verebilir miyim bileceğim.

Basit ve parlak
Harry

10

Basitçe söylemek gerekirse, C # verim anahtar kelimesi, yineleyici olarak adlandırılan, bitmeden önce nasıl döneceğini bilen ve tekrar çağrıldığında kaldığı yerden devam eden birçok kod gövdesine çağrı yapılmasına izin verir - yani bir yineleyiciye yardımcı olur art arda gelen aramalarda yineleyicinin döndürdüğü bir sırayla her öğe için şeffaf bir şekilde durum bilgisi olur.

JavaScript'te aynı konsepte Jeneratörler denir.


Şimdiye kadarki en iyi açıklama. Bunlar aynı zamanda python'da aynı jeneratörler mi?
petrosmm

7

Nesneniz için bir numaralandırmanın çok basit ve kolay bir yoludur. Derleyici, yönteminizi saran ve bu durumda IEnumerable <object> uygulayan bir sınıf oluşturur. Ürün anahtar kelimesi olmadan, IEnumerable <object> öğesini uygulayan bir nesne oluşturmanız gerekir.


5

Numaralandırılabilir dizi üretiyor. Yaptığı şey aslında yerel IEnumerable dizisi oluşturmak ve bunu bir yöntem sonucu olarak döndürmektir


3

Bu bağlantı basit bir örneği var

Daha basit örnekler burada

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

Verim getirisinin yöntemden geri dönmeyeceğine dikkat edin. Hatta birWriteLine sonrayield return

Yukarıdaki 4 ints 4,4,4,4 IEnumerable üretir

Burada a WriteLine. Listeye 4 ekleyecek, abc yazdıracak, sonra listeye 4 ekleyecek, daha sonra yöntemi tamamlayacak ve gerçekten yöntemden geri dönecektir (yöntem tamamlandıktan sonra, dönüş olmadan bir prosedürde olduğu gibi). Ancak bu , tamamlandığında döndürdüğü bir değere, bir s IEnumerablelistesine sahip olacaktır int.

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

Ayrıca, verimi kullandığınızda, döndürdüğünüz şeyin işlevle aynı türde olmadığına dikkat edin. IEnumerableListedeki bir öğe türündedir .

Verimi yöntemin dönüş türü olarak kullanırsınız IEnumerable. Yöntemin döndürme türü intya da List<int>kullanıyorsanız yield, derlemez. IEnumerableVerim olmadan yöntem dönüş türünü kullanabilirsiniz, ancak verim olmadan verimi kullanamayabilirsinizIEnumerable yöntem dönüş türünü .

Ve onu yürütmek için özel bir şekilde çağırmalısınız.

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");
}

not- public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { yield return t; }public static IEnumerable<TResult> testYieldc<TResult>(TResult t) { return new List<TResult>(); }
SelectMany'yi

Çok iyi bir açıklama gibi görünüyor! Bu kabul edilen cevap olabilirdi.
pongapundit

@pongapundit teşekkürler, cevabım kesinlikle açık ve basit, ama verimi çok fazla kullanmadım, diğer cevap verenler ve kullanımları hakkında benden daha fazla deneyime sahipler. Burada verim yazdığım şey muhtemelen burada ve o dotnetperls bağlantısında bazı cevapları anlamaya çalışırken başımı tırmalamak oldu! Ama bunu yield returniyi bilmediğim için (bahsettiğim basit şey dışında) ve çok fazla kullanmadım ve kullanımlarını çok fazla bilmediğim için, bunun kabul edilmiş olması gerektiğini düşünmüyorum.
barlop

3

Verim anahtar kelimesi hakkında önemli bir nokta Lazy Execution'dur . Şimdi Tembel İnfaz ile kastettiğim, gerektiğinde idam etmektir. Bunu koymanın daha iyi bir yolu bir örnek vermektir

Örnek: Verim kullanılmıyor yani Tembel Uygulama Yok.

        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;
        }

Örnek: Verim kullanımı, yani Tembel Uygulama.

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

        yield return 20;
    }

Şimdi her iki yöntemi de çağırdığımda.

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

listItems içinde 5 öğe olacak (hata ayıklama sırasında farenizi listItems üzerine getirin). Oysa giveItems öğelere değil, sadece yönteme bir referans olacaktır. Bu, yöntemin içine öğe alma işlemini gerçekleştirmediği anlamına gelir. Sadece gerektiğinde veri elde etmenin çok etkili bir yolu. Gerçek getirinin uygulanması Varlık Çerçevesi ve NHibernate gibi ORM'de görülebilir.


-3

Bazı Ruby Goodness getirmeye çalışıyor :)
Konsept: Bu, dizinin her öğesini basan bazı örnek Ruby Kodu

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

Dizi, her yöntem, uygulama bu verimler arayana üzerinde kontrol ile ( 'koyar x) her bir dizinin elemanının düzgün x olarak sunulmuştur. Arayan daha sonra x ile yapması gereken her şeyi yapabilir.

Ancak .Net buraya kadar gitmiyor .. C #, Mendelt'in cevabında görüldüğü gibi arayanda bir foreach döngüsü yazmaya zorlamak için IEnumerable ile verimi birleştirmiş gibi görünüyor. Biraz daha az zarif.

//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]; 
}

7
-1 Bu cevap bana doğru gelmiyor. Evet, C # yieldile birleştirilir IEnumerableve C # Ruby'nin bir "blok" kavramından yoksundur. Ancak C #, ForEachRuby'nin çok benzer şekilde bir yöntemin uygulanmasına izin verebilecek lambdas'a sahiptir each. Bu, bunun iyi bir fikir olacağı anlamına gelmez .
rsenna

Daha da iyisi: public IEnumerable <int> Each () {int index = 0; getiri verisi [index ++]; }
ata
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.