Aklımda Kalası Kelimeler

* давайте работать вместе
* Zarf ve Mazruf, Zerafet(xHoyratlık) ile aynı kökten(za-ra-fe) gelir
* Bedesten
* Suç subuta ermiştir - Suç sabit olmuştur

5 Ocak 2017 Perşembe

IEnumerable ve IEnumerator farkları nelerdir

IEnumerable interface'i bize hangi metodu yazmayı zorlar ona bir bakalım:
public interface IEnumerable
{
  IEnumerator GetEnumerator();
}

Peki bu metodu kim çağıracakki biz implemente edelim?
foreach(var eleman in kolleksiyon)
foreach Arka tarafta şunu yaratır:
var oteleyen = kolleksiyon.GetEnumerator();
while(oteleyen.MoveNext()) // true dönerse, eleman var demektir
{
  Console.WriteLine(oteleyen.Current); // eleman'ın ToString metodunu çağır ekrana yaz
}
oteleyen.Reset(); // kolleksiyonun içinde döndük sona geldik, başa dönmek için resetleyelim

Bu da güzeldi ancak biz sadece GetEnumerator metodunu anlayalım derken başımıza MoveNext(), Current ve Reset() çıktı!!!
Peki bize bu metotları sağlayan tip kimdir?
var oteleyen = kolleksiyon.GetEnuemerator(); // aslında şöyle de yazılabilir
IEnumerator oteleyen = kolleksiyon.GetEnuemerator(); 
Demekki tipimiz IEnumerator. Peki o zaman IEnumerator bize ne şartı koşuyor içine bakalım:
public interface IEnumerator
{
  object Current { get; } // Şimdi üstünde bulunduğum elemanı ver bakalım
  bool MoveNext();        // sonraki eleman mevcut mu?
  void Reset();           // en baştaki noktaya geri gidelim
}

Anlaşıldıki bizim forEach bir IEnumerator(numaralandırıcı) istiyor ve onun nimetleri olan metot ve özelliği kullanarak kolleksiyon içinde dönüyor duruyor.
Peki bu tipin generic olması bize ne kazandırır? O zaman generic IEnumerator tipine bakalım ve casting ile kaybettiğimiz performansı bakalım nasıl geri alıyoruz?
public interface IEnumerator : IDisposable, IEnumerator
{
  T Current { get; }  
  // Sadece Current için T tipini sağlıyor. 
  // Zaten Reset void döner, MoveNext ise bool geriye performans kaybı yaşatan Current kalıyor
  // Onuda T dönecek şekle getirirsek sorun giderilmiş olur.
}

Peki GetEnumerator() metodu bize IEnumerator dönüyordu. Onu da generic ile IEnumerator dönecek şekle getirmemiz gerekmez mi?
public interface IEnumerable : IEnumerable
{
  IEnumerator GetEnumerator();
}
Şimdi tip dönüşümlerinden kurtulduğumuza göre performans rahatlığıyla bu iki tipi kullanabiliriz.
Gelin kendimize bir örnek yazalım ve makalemizi sonlandıralım. Eğer anlaşılmayan yerler olursa lütfen yorumlarınızda sorun ;)
class Cem : IEnumerable, IEnumerator
{
    T[] arr = new T[0];
    int iCur = 0;

    int idx = -1;
    public T Current
    {
        get
        {
            return arr[idx];
        }
    }

    object IEnumerator.Current
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public void Add(T a) { 
      Array.Resize(ref arr, arr.Length + 1); // dizimizi bir arttıralım ki yeni elemanı ekleyebilelim
      arr[arr.Length-1] = a; // artık bir büyüdüğüne göre -1 ile sonuncu sıraya yeni elemanı yerleştirelim
    }

    public void Dispose() { }

    public IEnumerator GetEnumerator() { return this; }

    public bool MoveNext()
    {
        var next = idx + 1;
        var sonuc = (next < arr.Length);
        if (sonuc) idx = next;
        return sonuc;
    }

    public void Reset()
    {
        idx = -1;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
class Program
{
    static void Main(string[] args)
    {
        var c = new Cem();
        c.Add(22);
        c.Add(32);
        c.Add(42);
        c.Add(44);
        foreach (var a in c)
            Console.WriteLine(a);
    }
}

Hiç yorum yok: