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:

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().