C Sharp Programlama Dili/yield

Ders 34. yield


Hatırlarsanız foreach döngüsü şöyle kuruluyordu:

 foreach(Tür a in b)
 {
    ...
 }

Burada b'nin kendi yarattığımız bir sınıf türünden olması için bu kendi yarattığımız sınıfın IEnumerable arayüzünü uygulaması gerekiyordu. Bu arayüz geri dönüş tipi IEnumerator olan bir metot içeriyordu. Ve daha bir sürü karmaşık şey vardı. Şimdiye kadar IEnumerable ve IEnumerator arayüzlerinin nasıl birlikte çalıştığını anlamamış olabilirsiniz. Ancak aynı mantıkla çalışan çok daha basit bir program yazınca bunun nasıl olduğunu anlayacaksınız:

 interface i1
 {
    i2 Metot();
 }
 interface i2{}
 class Program:i1
 {
    public i2 Metot()
    {
       return new AltProgram();
    }
    class AltProgram:i2{}
    static void Main(){}
 }

Bu programda i1 arayüzü geri dönüş tipi i2 arayüzü olan Metot() isimli metodu içeriyor. Asıl sınıfımız i1 arayüzünü uyguluyor. Geri dönüş tipi i2 arayüzü olan Metot() metodumuzu sınıfımıza yerleştiriyoruz. Programımızdaki en kritik nokta metodumuzun geri dönüş tipi i2 arayüzü olmasına rağmen, bizim AltProgram sınıfı türünden bir nesne döndürmemiz. Çünkü AltProgram sınıfını incelerseniz bunun da i2 arayüzünü kullandığını göreceksiniz. Yani sınıflardaki türeme kuralları arayüz uygulamada da geçerli. Bir sınıf nesnesi, sınıfın kullandığı bir arayüz nesnesine atanabiliyor. Bu kuralı da şu basit programla özetleyebiliriz:

 interface Arayuz{}
 class Sinif:Arayuz{}
 class AnaProgram
 {
    static void Main()
    {
       Arayuz a;
       Sinif s=new Sinif();
       a=s;
    }
 }

Ancak bir arayüz nesnesini new operatörüyle oluşturamayız. Çünkü new operatörü nesne için bellekte yer açmak demektir. Arayüzlerin pratikte bellekte yer kaplaması imkansızdır. O yüzden derleyici new operatörüyle arayüz nesnelerinin oluşturulmasını engeller. Konu buraya gelmişken bir şey söylemek istiyorum. Aslında sadece tanım yapmak istiyorum:

 Sinif a;
 Sinif a=new Sinif();

Yukarıdaki iki satırdan birincisinde bir referans oluşturulur. İkincisinde ise nesne oluşturulur. Aslında bu ikisinin arasındaki farkı biliyordunuz, burada sadece bunların isimlerini söyledim. Şimdi konuyu fazla dağıtmadan Arayüzler konusunda gördüğümüz ve kendi oluşturduğumuz sınıf türünden bir nesnenin foreach döngüsüyle kullanılabilmesini sağlayan programı tekrar yazmak istiyorum:

 using System;
 using System.Collections;
 class Koleksiyon:IEnumerable
 {
    int[] Dizi;
    public Koleksiyon(int[] dizi)
    {
       this.Dizi=dizi;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
       return new ENumaralandırma(this);
    }
    class ENumaralandırma:IEnumerator
    {
       int indeks;
       Koleksiyon koleksiyon;
       public ENumaralandırma(Koleksiyon koleksiyon)
       {
          this.koleksiyon=koleksiyon;
          indeks=-1;
       }
       public void Reset()
       {
          indeks=-1;
       }
       public bool MoveNext()
       {
          indeks++;
          if(indeks<koleksiyon.Dizi.Length)
             return true;
          else
             return false;
       }
       object IEnumerator.Current
       {
          get
          {
             return(koleksiyon.Dizi[indeks]);
          }
       }
    }
 }
 class MainMetodu
 {
    static void Main()
    {
       int[] dizi={1,2,3,8,6,9,7};
       Koleksiyon k=new Koleksiyon(dizi);
       foreach(int i in k)
          Console.Write(i+" ");
    }
 }

Burada az önceki basit örneklerde gördüğümüz kurallar var.

Az kalsın hatırlatma yapacağız derken asıl konumuzu unutuyorduk.:) İşte şimdi asıl konumuza başlıyoruz. yield deyimleri kendi oluşturduğumuz sınıf türünden nesnelerin kısa yoldan foreach döngüsüyle kullanılabilmesini sağlar. Örnek program:

 using System;
 using System.Collections;
 class Sinif:IEnumerable
 {
    public IEnumerator GetEnumerator()
    {
       yield return 1;
       yield return 2;
       yield return 3;
    }
 }
 class Program
 {
    static void Main()
    {
       Sinif s=new Sinif();
       foreach(int i in s)
          Console.WriteLine(i);
    }
 }

Bu programın ekran çıktısı alt alta 1, 2 ve 3 olacaktır. Gördüğünüz gibi IEnumerator sınıfını yazmamıza gerek kalmadı. Her yield return sözcüğü bir iterasyon belirtir. Bu örneğimizde yield return anahtar sözcüğünün yanında int türden nesne koyduk. Ancak her türden nesne koyabilirsiniz. yield return anahtar sözcükleri mutlaka geri dönüş tipi IEnumerator olan bir metodun içinde kullanılmalıdır. Başka bir örnek:

 using System;
 using System.Collections.Generic;
 using System.Collections;
 class Say:IEnumerable<int>
 {
    public int KacarKacar;
    public int KactaBitsin;
    IEnumerator<int> IEnumerable<int>.GetEnumerator()
    {
       for(int a=0;a<=KactaBitsin;a+=KacarKacar)
          yield return a;
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
       return null;
    }
 }
 class AnaProgram
 {
    static void Main()
    {
       Say s=new Say();
       s.KacarKacar=10;
       s.KactaBitsin=150;
       foreach(int i in s)
          Console.WriteLine(i);
    }
 }

Bu program 0'dan 150'ye kadar sayıları 10'ar 10'ar alt alta yazacaktır. yield return anahtar sözcüğüne benzer çalışan bir de yield break anahtar sözcüğü vardır. yield return ile yeni bir iterasyon başlatılırken yield break ile tüm iterasyonlardan çıkılır. Bu anahtar sözcüğü döngülerdeki break anahtar sözcüğüne benzetebiliriz. yield return'ü ise continue'ye benzetebiliriz. Örnek:

 using System;
 using System.Collections;
 class Sinif:IEnumerable
 {
    public IEnumerator GetEnumerator()
    {
       yield return 1;
       yield return 2;
       yield break;
       yield return 3;
    }
 }
 class Program
 {
    static void Main()
    {
       Sinif s=new Sinif();
       foreach(int i in s)
          Console.WriteLine(i);
    }
 }

Bu programla ekrana 1 ve 2 yazılır, 3 yazılmaz.

Bu kitabın diğer sayfaları
  • Sınıflar
  • Operatör aşırı yükleme
  • İndeksleyiciler
  • Yapılar
  • Enum sabitleri
  • İsim alanları
  • System isim alanı
  • Temel I/O işlemleri
  • Temel string işlemleri
  • Kalıtım
  • Arayüzler
  • Kısmi tipler ve metotlar
  • İstisnai durum yakalama mekanizması
  • Temsilciler
  • Olaylar
  • Önişlemci komutları
  • Göstericiler
  • Assembly kavramı
  • Yansıma
  • Nitelikler
  • Örnekler
  • Şablon tipler
  • Koleksiyonlar
  • yield
  • Veri tabanı işlemleri
  • XML işlemleri
  • Form tabanlı uygulamalar
  • Visual Studio.NET
  • Çok kanallı uygulamalar
  • ASP.NET