17 Mart 2012 Cumartesi

C# ile Delegate

Merhaba değerli Webkolog.net takipçileri, bugünkü yazımızda C# programlama dilinin güçlü ve esnek özelliklerinden biri olan Delegate'leri (Temsilcileri) yakından inceleyeceğiz. .NET Framework 4 ile birlikte gelen bu yapı, metotları tıpkı değişkenler gibi ele almamıza, metotları parametre olarak geçirmemize ve olay (event) mekanizmalarının temelini oluşturmamıza olanak tanır. Delegate'ler sayesinde daha dinamik, esnek ve olay güdümlü uygulamalar geliştirebiliriz. Gelin, delegate'lerin ne olduğuna, nasıl tanımlandığına ve farklı kullanım senaryolarına birlikte göz atalım.

Delegate Nedir?

En basit tanımıyla Delegate, metotların imzalarını (dönüş tipi ve parametre tipleri) tanımlayan bir türdür. Başka bir deyişle, bir delegate bir metot işaretçisidir. Nasıl bir integer değişkeni bir tam sayı değerini tutarsa, bir delegate değişkeni de belirli bir imzaya sahip bir metodu tutabilir. Bu sayede metotları sanki birer veriymiş gibi işleyebiliriz.

Delegate Tanımlama

Delegate tanımlamak için delegate anahtar kelimesini kullanırız. Delegate tanımı, temsil edeceği metotların dönüş tipini ve parametre listesini belirtir.

public delegate bool DiziIslemleri(string s);

Bu örnekte, DiziIslemleri adında bir delegate tanımlıyoruz. Bu delegate, bir string parametre alan ve bir bool değer döndüren metotları temsil edebilir.

Delegate Kullanımı: Metotları Parametre Olarak Geçirme

Delegate'lerin en önemli kullanım alanlarından biri, metotları başka metotlara parametre olarak geçirmektir. Bu sayede, bir işlemin farklı adımlarını farklı metotlarla tanımlayıp, bu metotları genel bir işleyiciye (handler) dinamik olarak gönderebiliriz.

Aşağıdaki örnekte, bir string dizisini belirli bir kritere göre filtreleyen IslemMetodu adında genel bir metot tanımlıyoruz. Filtreleme kriteri ise DiziIslemleri delegate'i aracılığıyla metoda parametre olarak geçiriliyor.

using System;
using System.Collections; // ArrayList için gerekli

public delegate bool DiziIslemleri(string s);

public class Program
{
    public static bool KileBaslayan(string s) { return s.StartsWith("K"); }
    public static bool AileBiten(string s) { return s.EndsWith("a"); }

    public static string[] IslemMetodu(string[] dizi, DiziIslemleri fonk)
    {
        ArrayList al = new ArrayList();
        foreach (string item in dizi) { if (fonk(item)) { al.Add(item); } }
        return (string[])al.ToArray(typeof(string));
    }

    static void Main(string[] args)
    {
        string[] sehirler = { "İstanbul", "Ankara", "Erzurum", "Elazığ", "İzmir", "Kastamonu", "Kayseri", "Kilis", "Gaziantep", "Antalya", "Manisa", "Malatya", "Sakarya", "Samsun", "Edirne", "Erzincan", "Tunceli", "Eskişehir" };
        string[] ySehirler;

        Console.WriteLine("#### Şehirler");
        foreach (string item in sehirler) { Console.WriteLine(item); }
        Console.WriteLine();

        ySehirler = IslemMetodu(sehirler, KileBaslayan); // K ile başlayan şehirleri filtrele
        Console.WriteLine("#### K ile başlayan:");
        foreach (string item in ySehirler) { Console.WriteLine(item); }
        Console.WriteLine();

        ySehirler = IslemMetodu(sehirler, AileBiten); // A ile biten şehirleri filtrele
        Console.WriteLine("#### A ile biten:");
        foreach (string item in ySehirler) { Console.WriteLine(item); }
        Console.WriteLine();

        // Anonim metot ile delegate kullanımı
        DiziIslemleri delegeAnonim = delegate(string s) { return s.StartsWith("A"); };
        ySehirler = IslemMetodu(sehirler, delegeAnonim);
        Console.WriteLine("#### A ile başlayan (anonim metot):");
        foreach (string item in ySehirler) { Console.WriteLine(item); }
        Console.WriteLine();

        // Anonim metotla diğer kullanım
        ySehirler = IslemMetodu(sehirler, delegate(string s) { return s.EndsWith("K"); });
        Console.WriteLine("#### K ile biten (anonim metot):");
        foreach (string item in ySehirler) { Console.WriteLine(item); }
        Console.WriteLine();

        // Lambda kullanarak
        ySehirler = IslemMetodu(sehirler, (s => s.EndsWith("i"))); // Örnek olarak 'i' ile biten
        Console.WriteLine("#### 'i' ile biten (lambda):");
        foreach (string item in ySehirler) { Console.WriteLine(item); }

        Console.ReadLine(); // Konsolun kapanmasını beklemek için
    }
}

Bu örnekte, IslemMetodu metodu, farklı filtreleme mantıklarını içeren KileBaslayan ve AileBiten metotlarını delegate aracılığıyla parametre olarak alarak, diziyi farklı kriterlere göre filtreleyebiliyor. Bu, kodun tekrar kullanılabilirliğini ve esnekliğini önemli ölçüde artırır.

Anonim Metotlar ile Delegate Kullanımı

Delegate'leri kullanmanın bir diğer yolu da anonim metotlardır. Anonim metotlar, isimsiz olarak tanımlanan ve doğrudan delegate değişkenine atanan kod bloklarıdır. Bu özellik C# 2.0 ile gelmiştir ve .NET Framework 4'te rahatlıkla kullanabilirsiniz.

// Anonim metot ile delegate kullanımı
DiziIslemleri delege = delegate(string s) { return s.StartsWith("A"); };
ySehirler = IslemMetodu(sehirler, delege);
// ... Çıktı kısmı ...

// Anonim metotla diğer kullanım
ySehirler = IslemMetodu(sehirler, delegate(string s) { return s.EndsWith("K"); });
// ... Çıktı kısmı ...

Anonim metotlar, basit ve tek seferlik metot tanımlamaları için oldukça pratiktir.

Lambda İfadeleri ile Delegate Kullanımı

C# 3.0 ile birlikte gelen Lambda ifadeleri, anonim metotlara göre daha kısa ve öz bir söz dizimi sunar. Lambda ifadeleri de delegate'lerle birlikte sıklıkla kullanılır ve .NET Framework 4'te mevcuttur.

// Lambda kullanarak
ySehirler = IslemMetodu(sehirler, (s => s.EndsWith("K")));
// ... Çıktı kısmı ...

Bu örnekte (s => s.EndsWith("K")) ifadesi, bir string parametre alan (s) ve s.EndsWith("K") sonucunu (bir bool değer) döndüren anonim bir metodu temsil eder.

Class'lar ile Delegate Kullanımı: Generic Delegate'ler

Delegate'ler sadece basit veri tipleriyle değil, class'larla da birlikte kullanılabilir. Generic delegate'ler, farklı tiplerdeki nesnelerle çalışabilen genel amaçlı delegate'lerdir. Generics özelliği C# 2.0 ile gelmiştir.

Aşağıdaki örnekte, Siralama<Tip> adında generic bir delegate tanımlıyoruz. Bu delegate, aynı tipten iki nesne alan ve bir bool değer döndüren metotları temsil edebilir. Ardından, BubbleSort sınıfında generic bir sıralama metodu ve farklı sıralama kriterlerini içeren statik metotlar tanımlıyoruz.

using System;
using System.Collections.Generic; // List için gerekli

namespace ConsoleApplication3
{
    delegate bool Siralama<Tip>(Tip s1, Tip s2);

    class BubbleSort
    {
        static public bool SatisMiktarSirala(Satis s1, Satis s2) { return s1.Miktar < s2.Miktar; }
        static public bool SatisTarihSirala(Satis s1, Satis s2) { return s1.Tarih < s2.Tarih; }

        static public void Sirala<Tip>(List<Tip> sl, Siralama<Tip> fonk)
        {
            bool sirali = true;
            do
            {
                sirali = false;
                for (int i = 0; i < sl.Count - 1; i++)
                {
                    if (fonk(sl[i + 1], sl[i]))
                    {
                        Tip temp = sl[i];
                        sl[i] = sl[i + 1];
                        sl[i + 1] = temp;
                        sirali = true;
                    }
                }
            } while (sirali);
        }
    }

    class Satis
    {
        private int miktar;
        private DateTime tarih;
        public int Miktar { get { return miktar; } set { miktar = value; } }
        public DateTime Tarih { get { return tarih; } set { tarih = value; } }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Satis> satislar = new List<Satis> {
                new Satis{Miktar=5,Tarih=DateTime.Parse("01/01/2010")},
                new Satis{Miktar=1,Tarih=DateTime.Parse("28/12/2014")},
                new Satis{Miktar=6,Tarih=DateTime.Parse("08/03/2012")},
                new Satis{Miktar=2,Tarih=DateTime.Parse("05/11/2015")},
                new Satis{Miktar=3,Tarih=DateTime.Parse("21/07/2000")},
                new Satis{Miktar=4,Tarih=DateTime.Parse("07/08/2011")},
                new Satis{Miktar=12,Tarih=DateTime.Parse("13/02/2013")}
            };

            Console.WriteLine("#Normal:");
            foreach (var item in satislar) { Console.WriteLine("Tarih: {1}, Miktar: {0}", item.Miktar, item.Tarih); }
            Console.WriteLine();

            BubbleSort.Sirala<Satis>(satislar, BubbleSort.SatisMiktarSirala);
            Console.WriteLine("# Miktara göre:");
            foreach (var item in satislar) { Console.WriteLine("Tarih: {1}, Miktar: {0}", item.Miktar, item.Tarih); }
            Console.WriteLine();

            BubbleSort.Sirala<Satis>(satislar, BubbleSort.SatisTarihSirala);
            Console.WriteLine("# Tarihe göre:");
            foreach (var item in satislar) { Console.WriteLine("Tarih: {1}, Miktar: {0}", item.Miktar, item.Tarih); }

            // Anonim metot ile sıralama
            Console.WriteLine();
            BubbleSort.Sirala<Satis>(satislar, delegate(Satis s1, Satis s2) { return s1.Tarih > s2.Tarih; });
            Console.WriteLine("# Tarihe göre (anonim):");
            foreach (var item in satislar) { Console.WriteLine("Tarih: {1}, Miktar: {0}", item.Miktar, item.Tarih); }

            // Lambda ile sıralama
            Console.WriteLine();
            BubbleSort.Sirala<Satis>(satislar, (s1, s2) => s1.Tarih > s2.Tarih);
            Console.WriteLine("# Tarihe göre (lambda):");
            foreach (var item in satislar) { Console.WriteLine("Tarih: {1}, Miktar: {0}", item.Miktar, item.Tarih); }

            Console.ReadLine();
        }
    }
}

Bu örnekte, Sirala metodu, farklı Siralama<Satis> delegate örnekleri (örneğin SatisMiktarSirala, SatisTarihSirala veya anonim metot/lambda ifadesi) alarak Satis nesnelerini farklı kriterlere göre sıralayabilir.

Önceden Tanımlanmış Delegate Tipleri: Action ve Func

.NET Framework, sıklıkla kullanılan delegate imzaları için önceden tanımlanmış generic delegate tipleri sunar. Bu tipler C# 3.0 ile gelmiştir ve .NET Framework 4'te kullanılabilir:

  • Action: Parametre alan ancak herhangi bir değer döndürmeyen metotları temsil eder (Action<T>, Action<T1, T2>, vb.).
  • Func: Parametre alan ve bir değer döndüren metotları temsil eder (Func<T, TResult>, Func<T1, T2, TResult>, vb.).

Bu önceden tanımlanmış delegate tipleri, kendi delegate'lerinizi tanımlama ihtiyacını azaltabilir ve kodunuzu daha kısa ve okunabilir hale getirebilir.

---

Evet sevgili Webkolog.net okurları, bu yazımızda C# .NET Framework 4 ile Delegate'lerin ne olduğunu, nasıl tanımlandığını ve farklı kullanım senaryolarını detaylı bir şekilde inceledik. Delegate'ler, metotları değişken gibi kullanmamıza olanak tanıyarak uygulamalarımıza büyük bir esneklik ve dinamizm katarlar. Olay güdümlü programlama, asenkron işlemler ve genel amaçlı algoritmalar geliştirirken delegate'lerin gücünden sıklıkla yararlanırız. Umarım bu rehber, delegate kavramını anlamanıza ve projelerinizde etkin bir şekilde kullanmanıza yardımcı olur. Bir sonraki yazımda, C# dilinin diğer önemli yapı taşlarını keşfetmeye devam edeceğiz. Webkolog.net'i takipte kalın!

Hepinize delegate'lerle dolu ve esnek kodlama deneyimleri dilerim!

0 yorum:

Yorum Gönder