“Verim kırılması” ne yapar; C # yapmak?


494

MSDN'de bu sözdizimini gördüm:, yield breakama ne yaptığını bilmiyorum. Kimse biliyor mu?


26
Ancak, MSDN belgelerinde bundan bahsediliyor ancak açıklanmıyor. Bu bilgi aç geliştirici alay etmek için kötü bir yoldur.
Phil

3
Getiri dönüşü, bir destek listesi ihtiyacını ortadan kaldırır, yani MyList.Add(...)sadece yaptığınız gibi bir şeyi kodlamanız gerekmez yield return .... Döngüden erken ayrılmanız ve kullandığınız sanal destek listesini döndürmeniz gerekiyorsayield break;
The Muffin Man

Yanıtlar:


529

Bir yineleyicinin sona erdiğini belirtir. Bir değer döndürmeyen bir ifade yield breakolarak düşünebilirsiniz return.

Örneğin, bir işlevi yineleyici olarak tanımlarsanız, işlevin gövdesi şöyle görünebilir:

for (int i = 0; i < 5; i++)
{
    yield return i;
}

Console.Out.WriteLine("You will see me");

Döngü tüm döngülerini tamamladıktan sonra, son satırın yürütüleceğini ve mesajı konsol uygulamanızda göreceğinizi unutmayın.

Veya şu şekilde yield break:

int i = 0;
while (true)
{
    if (i < 5)
    {
        yield return i;
    }
    else
    {
        // note that i++ will not be executed after this
        yield break;
    }
    i++;
}

Console.Out.WriteLine("Won't see me");

Bu durumda, işlevi erken bıraktığımız için son ifade asla çalıştırılmaz.


8
Yukarıdaki örneğiniz breakyerine basit olabilir yield breakmi? Derleyici bundan şikayetçi değil.
orad

85
@ breakbu durumda basit bir döngü döngüyü durduracaktı, ancak yöntem yürütmeyi iptal etmeyecekti, bu nedenle son satır yürütülecek ve "Beni metin görmeyeceğim" görülecektir.
Damir Zekić

5
@Damir Zekić Cevabınıza geri dönüş üzerinde verim kırılmasını neden tercih etmelisiniz ve ikisi arasındaki farklar nelerdir?
Bruno Costa

11
@ DamirZekić null ve getiri sonu döndürme aynı değildir . Null NullReferenceExceptiondeğerini döndürürseniz, IEnumerable'un numaralandırıcısına sahip olabilirsiniz, verim molası ile bunu yapmazsınız (öğesiz bir örnek vardır).
Bruno Costa

6
@BrunoCosta Aynı yöntemde düzenli geri dönüşler yapmaya çalışmak derleyici hatası verir. yield return xDerleyiciyi kullanmak , bu yöntemin bir Enumeratornesne oluşturmak için sözdizimsel şeker olmasını istediğinizi bildirir . Bu Numaralandırıcı'nın yöntemi MoveNext()ve özelliği vardır Current. MoveNext () yöntemi bir yield returndeyime kadar çalıştırır ve bu değeri dönüştürür Current. MoveNext bir sonraki çağrıldığında yürütme oradan devam eder. yield breakAkımı null değerine ayarlar, bu numaralandırıcının foreach (var x in myEnum())sonunu bildirir, böylece bir bitirilir.
Wolfzoon

57

Bir yineleyici bloğunu sonlandırır (örneğin, IEnumerable'da başka öğe olmadığını söyler).


2
[+1] ile - akademik olarak .NET'te yineleyici olmamasına rağmen. sadece numaralandırıcılar (tek yön, ileri.)
Shaun Wilson

@Shaun Wilson, ancak yieldanahtar kelime ile koleksiyonu her iki yönde de yineleyebilirsiniz, üstelik koleksiyonun her sonraki öğesini üst üste değil alabilirsiniz
monstr

@monstr koleksiyonu herhangi bir şekilde yineleyebilirsiniz ve bunu yapmanız gerekmez yield. yanlış olduğunu söylemiyorum, ne önerdiğini anlıyorum; ancak, akademik olarak .NET'te yineleyiciler yoktur, yalnızca numaralandırıcılar (bir yönde, ileri) - stdc ++ 'dan farklı olarak CTS / CLR'de tanımlanmış bir "genel yineleyici çerçevesi" yoktur. LINQ, boşluğu kullanan uzantı yöntemleriyle yield return ve aynı zamanda geri çağrı yöntemleriyle boşluğun kapatılmasına yardımcı olur , ancak bunlar birinci sınıf yineleyiciler değil, birinci sınıf genişletme yöntemleridir. sonuçta IEnumerablekendisi arayandan ileriye doğru başka bir yönde yinelenemez.
Shaun Wilson

29

Yineleyiciye sonuna geldiğini söyler.

Örnek olarak:

public interface INode
{
    IEnumerable<Node> GetChildren();
}

public class NodeWithTenChildren : INode
{
    private Node[] m_children = new Node[10];

    public IEnumerable<Node> GetChildren()
    {
        for( int n = 0; n < 10; ++n )
        {
            yield return m_children[ n ];
        }
    }
}

public class NodeWithNoChildren : INode
{
    public IEnumerable<Node> GetChildren()
    {
        yield break;
    }
}

21

yieldtemel olarak bir IEnumerable<T>yöntemin, işbirlikçi (önleyici olarak değil) zamanlanmış bir iş parçacığına benzer şekilde davranmasını sağlar.

yield returnCPU'nun kontrolünden vazgeçmek için "zamanlama" veya "uyku" işlevini çağıran bir evre gibidir. Tıpkı bir iş parçacığı gibi, IEnumerable<T>yöntem de hemen ardından noktada kontrolleri yeniden kazanır ve tüm yerel değişkenler kontrolden vazgeçilmeden önceki değerlerle aynı değerlere sahiptir.

yield break işlevinin sonuna ulaşan ve sonlanan bir iplik gibidir.

İnsanlar bir "devlet makinesi" hakkında konuşur, ancak bir devlet makinesi hepsi bir "iplik" gerçekten. Bir iş parçacığının durumu (yerel değişkenlerin Ie değerleri) vardır ve her programlandığında yeni bir duruma ulaşmak için bazı eylemler gerçekleştirir. Temel nokta yield, alıştığımız işletim sistemi iş parçacıklarının aksine, onu kullanan kodun yineleme manuel olarak ilerletilinceye veya sonlandırılana kadar zaman içinde dondurulmasıdır.



9

yield breakİfadesi durağına numaralandırma neden olur. Aslında, yield breakherhangi bir ek öğe iade etmeden numaralandırmayı tamamlar.

Bir yineleyici yönteminin yinelemeyi durdurabilmesinin aslında iki yolu olduğunu düşünün. Bir durumda, yöntemin mantığı tüm öğeleri döndürdükten sonra doğal olarak yöntemden çıkabilir. İşte bir örnek:

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount)
{
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;
    }

    Debug.WriteLine("All the primes were found.");
}

Yukarıdaki örnekte, yineleyiciler yöntemi, maxCountprimerler bulunduğunda doğal olarak yürütülmeyi durduracaktır .

yield breakDeyim yineleyici numaralandırma durdurma için başka bir yoludur. Bu numaralandırmadan erken kurtulmanın bir yoludur. İşte yukarıdaki ile aynı yöntem. Bu kez, yöntemin yürütülebileceği süre için bir sınırı vardır.

IEnumerable<uint> FindPrimes(uint startAt, uint maxCount, int maxMinutes)
{
    var sw = System.Diagnostics.Stopwatch.StartNew();
    for (var i = 0UL; i < maxCount; i++)
    {
        startAt = NextPrime(startAt);
        yield return startAt;

        if (sw.Elapsed.TotalMinutes > maxMinutes)
            yield break;
    }

    Debug.WriteLine("All the primes were found.");
}

Çağrıya dikkat edin yield break. Aslında, numaralandırmadan erken çıkılıyor.

Dikkat edin, yield breaksadece bir ovadan farklı çalışır break. Yukarıdaki örnekte, yield breakçağrıyı yapmadan yöntemden çıkar Debug.WriteLine(..).


7

Burada http://www.alteridem.net/2007/08/22/the-yield-statement-in-c/ çok iyi bir örnek:

public static IEnumerable <int> Aralık (int min, int max)
{
   while (true)
   {
      eğer (min> = maks)
      {
         verim kırılması;
      }
      verim dönüş min ++;
   }
}

ve yield breakbir yöntemin içinde bir deyime isabet edilirse, bu yöntemin yürütülmesinin herhangi bir dönüş olmadan durduğunu açıklar. Bazı zaman durumları vardır, herhangi bir sonuç vermek istemediğinizde, verim molası kullanabilirsiniz.


5

getiri sonu, son kez getiri söylemenin bir yoludur ve herhangi bir değer döndürmeyin

Örneğin

// returns 1,2,3,4,5
IEnumerable<int> CountToFive()
{
    yield return 1;
    yield return 2;
    yield return 3;
    yield return 4;
    yield return 5;
    yield break;
    yield return 6;
    yield return 7;
    yield return 8;
    yield return 9;
 }

4

"Verim molası gerçekten ne işe yarıyor" ile kastettiğiniz şey "nasıl çalışır?" - Ayrıntılar için Raymond Chen'in bloguna bakın https://devblogs.microsoft.com/oldnewthing/20080812-00/?p=21273

C # yineleyicileri çok karmaşık bazı kodlar üretir.


12
Eh, sadece derlendikleri kodu önemsiyorsanız karmaşıktırlar. Bunları kullanan kod oldukça basittir.
Robert Rossney

@Robert, demek istediğim bu yüzden yorumunuzu dahil etmek için cevabı güncelledim
Tony Lee

-2

Verim anahtar sözcüğü, enumerator nesnesine bir değer sağlamak için return anahtar sözcüğü ile birlikte kullanılır. verim dönüşü döndürülen değeri veya değerleri belirtir. Getiri beyanı ifadesine ulaşıldığında, geçerli konum saklanır. Tekrarlayıcı bir sonraki çağrıldığında yürütme bu konumdan yeniden başlatılır.

Bir örnek kullanarak anlamı açıklamak için:

    public IEnumerable<int> SampleNumbers()
    {
        int counter = 0;
        yield return counter;

        counter = counter + 2;

        yield return counter;

        counter = counter + 3;

        yield return counter ;
    }

Bu tekrarlandığında döndürülen değerler şunlardır: 0, 2, 5.

Bu örnekteki sayaç değişkeninin yerel bir değişken olduğuna dikkat etmek önemlidir . 2 değerini döndüren ikinci yinelemeden sonra, üçüncü yineleme daha önce kaldığı yerden başlar ve 2 olan counter adlı yerel değişkenin önceki değerini korurken .


11
Sen anlatmak değildi yield breakdoes
Jay Sullivan

yield returnAslında birden fazla değer döndürmeyi desteklediğini sanmıyorum . Belki aslında demek istediğin bu değil, ama ben böyle okudum.
Sam

Sam - çoklu getiri ifadeleri içeren SampleNumbers yöntemi aslında işe yarıyor, yineleyicinin değeri hemen döndürülüyor ve bir sonraki değer istendiğinde yürütme devam ediyor. İnsanların "verim kırılması" ile böyle bir yöntemi sonlandırdığını gördüm, ama gereksiz. Yöntemin sonuna isabet eden yineleyici de sona erer
Loren Paulsen

bunun kötü bir örnek olmasının nedeni, bir yield breaknumaralandırıcı foreachkullanıldığında yield breakgerçek değer sağlayan a gibi dil düzeyinde bir numaralandırıcı içermemesidir . bu örnek kaydedilmemiş bir döngüye benziyor. Bu kodu neredeyse hiç gerçek dünyada görmeyeceksiniz (hepimiz bazı uç vakaları düşünebiliriz, elbette), burada da "yineleyici" yoktur. "yineleyici bloğu" dil spesifikasyonu konusu yöntemin ötesine geçemez. gerçekte iade edilen bir "numaralandırılabilir", ayrıca bakınız: stackoverflow.com/questions/742497/…
Shaun Wilson
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.