Neler yeni

Yazılım Forum

Tüm özelliklerimize erişmek için şimdi bize katılın. Kayıt olduktan ve giriş yaptıktan sonra konu oluşturabilecek, mevcut konulara yanıt gönderebilecek, itibar kazanabilecek, özel mesajlaşmaya erişebilecek ve çok daha fazlasını yapabileceksiniz! Bu hizmetlerimiz ise tamamen ücretsiz ve kurallara uyulduğu sürece sınırsızdır, o zaman ne bekliyorsunuz? Hadi, sizde aramıza katılın!

C# Delegeler ve Olaylar: Güçlü ve Esnek Kod Yazmanın Temelleri

C# programlamada delegeler ve olaylar, kodunuzu daha esnek, modüler ve yönetilebilir hale getiren temel kavramlardır. Özellikle olay tabanlı programlamada (event-driven programming) ve .NET çatısı içinde birçok tasarım deseni (design pattern) uygulamasında kritik rol oynarlar. Bu makalede, delegelerin ne olduğunu, nasıl kullanıldığını, olayların delegelerle olan ilişkisini ve gerçek dünya senaryolarında nasıl uygulandıklarını derinlemesine inceleyeceğiz. Amacımız, bu güçlü araçları C# projelerinizde etkin bir şekilde kullanabilmeniz için kapsamlı bir rehber sunmaktır.

Delegeler (Delegates): Fonksiyon İşaretçileri Gibi
Delegeler, bir veya daha fazla metodu güvenli bir şekilde referans alabilen tür-güvenli (type-safe) bir işaretçi gibidir. C++'daki fonksiyon işaretçilerine benzerler, ancak .NET ortamının sağladığı güvenlik ve nesne yönelimli özelliklerle birleşirler. Bir delege, belirli bir imzaya (parametre tipleri ve dönüş değeri) sahip herhangi bir metodu işaret edebilir.

Delege Tanımlama:
Bir delege tanımlarken, delegeye atanabilecek metodun imzasını belirtiriz.
Kod:
public delegate void MyDelegate(string message);
Yukarıdaki tanım, string bir parametre alan ve hiçbir değer döndürmeyen (void) metodları işaret edebilecek bir delege türü oluşturur.

Delege Kullanımı:
Delege tanımlandıktan sonra, onu bir sınıf içinde bir alan, özellik veya yerel değişken olarak kullanabiliriz.
Kod:
public class Notifier
{
    public delegate void NotificationDelegate(string message);
    public NotificationDelegate NotifyUser;

    public void SendNotification(string message)
    {
        // Delege null değilse metodu çağır
        NotifyUser?.Invoke(message);
    }
}

public class Program
{
    public static void DisplayMessage(string msg)
    {
        Console.WriteLine($"[BİLGİ]: {msg}");
    }

    public static void LogMessage(string msg)
    {
        Console.WriteLine($"[LOG]: {msg} - {DateTime.Now}");
    }

    public static void Main(string[] args)
    {
        Notifier notifier = new Notifier();

        // Metodu delegeye atama
        notifier.NotifyUser = DisplayMessage;
        notifier.SendNotification("Merhaba Dünya!");

        // Birden fazla metodu delegeye atama (Multicast Delegates)
        notifier.NotifyUser += LogMessage; // Yeni metod ekle
        notifier.SendNotification("Çoklu yayın testi.");

        // Bir metodu delege listesinden çıkarma
        notifier.NotifyUser -= DisplayMessage;
        notifier.SendNotification("DisplayMessage çıkarıldı.");
    }
}
Yukarıdaki örnekte, NotifyUser delegesi hem DisplayMessage hem de LogMessage metodunu aynı anda işaret edebilmektedir. Bu duruma çoklu yayın delegesi (multicast delegate) denir. Bir delegeye `+=` operatörü ile metod ekleyebilir, `-=` operatörü ile metod çıkarabilirsiniz. Delegeler, abonelik listeleri oluşturmak için çok güçlü bir temel sağlar.

Built-in Delegeler: Func, Action ve Predicate
C# 3.0 ile birlikte gelen ve özellikle LINQ gibi modern özelliklerle sıkça kullanılan genel (generic) delegeler, çoğu zaman kendi delegelerinizi tanımlama ihtiyacınızı ortadan kaldırır.
  • Action: Geri dönüş değeri olmayan (void) metodlar için kullanılır. 0 ila 16 arasında parametre alabilir.
    Kod:
        Action<string> printAction = (msg) => Console.WriteLine(msg);
        printAction("Bu bir Action delegesi.");
  • Func: Geri dönüş değeri olan (non-void) metodlar için kullanılır. Son tip parametresi geri dönüş tipini belirtir. 0 ila 16 arasında parametre alabilir.
    Kod:
        Func<int, int, int> addFunc = (a, b) => a + b;
        Console.WriteLine($"Toplama sonucu: {addFunc(5, 3)}");
    
        Func<string, bool> isLongString = (s) => s.Length > 10;
        Console.WriteLine($"'Merhaba' uzun mu? {isLongString("Merhaba")}");
        Console.WriteLine($"'Çok uzun bir metin' uzun mu? {isLongString("Çok uzun bir metin")}");
  • Predicate: Yalnızca tek bir parametre alan ve `bool` geri dönüş tipi olan özel bir `Func` türevidir. Genellikle koleksiyonlarda filtreleme için kullanılır.
    Kod:
        Predicate<string> startsWithA = (s) => s.StartsWith("A");
        List<string> names = new List<string> { "Ahmet", "Ayşe", "Mehmet" };
        string foundName = names.Find(startsWithA);
        Console.WriteLine($"'A' ile başlayan isim: {foundName}");

Olaylar (Events): Abone Olma ve Bildirim Mekanizması
Olaylar, delegeler üzerine inşa edilmiş özel bir dil yapısıdır. Bir nesnenin, belirli bir şey olduğunda diğer nesneleri bilgilendirmesine olanak tanır. Örneğin, bir butona tıklandığında, bir veri yüklendiğinde veya bir işlem tamamlandığında. Olaylar, bir yayıncı (publisher) ve bir veya daha fazla abone (subscriber) arasındaki iletişimi kolaylaştırır.

Temel Felsefe: Yayıncı, olayın ne zaman gerçekleştiğini bilir ve olayı "yayınlar" (raises). Aboneler, bu olaya ilgi duyduklarında abone olurlar ve olay yayınlandığında bilgilendirilirler. Yayıncı, abonelerin kim olduğunu bilmez, yalnızca bir olayın gerçekleştiğini duyurur. Bu, gevşek bağlılık (loose coupling) sağlar.

Olay Tanımlama:
Bir olay, event anahtar kelimesi kullanılarak ve bir delege türü ile birlikte tanımlanır.
Kod:
public class Sensor
{
    // Özel bir delege tanımlayabiliriz
    public delegate void TemperatureChangeHandler(object sender, TemperatureEventArgs e);

    // Veya .NET'in yerleşik EventHandler delegelerini kullanabiliriz
    public event TemperatureChangeHandler TemperatureChanged;
    // Ya da standart EventHandler<TEventArgs>
    public event EventHandler<TemperatureEventArgs> HumidityChanged;

    private int _temperature;
    public int Temperature
    {
        get { return _temperature; }
        set
        {
            if (_temperature != value)
            {
                _temperature = value;
                // Olayı tetikle
                OnTemperatureChanged(value);
            }
        }
    }

    private int _humidity;
    public int Humidity
    {
        get { return _humidity; }
        set
        {
            if (_humidity != value)
            {
                _humidity = value;
                // Olayı tetikle
                OnHumidityChanged(value);
            }
        }
    }

    // Olay tetikleme metodu (genellikle "On" önekiyle)
    protected virtual void OnTemperatureChanged(int newTemperature)
    {
        // Olayın aboneleri var mı kontrol et
        TemperatureChanged?.Invoke(this, new TemperatureEventArgs(newTemperature));
    }

    protected virtual void OnHumidityChanged(int newHumidity)
    {
        HumidityChanged?.Invoke(this, new TemperatureEventArgs(newHumidity)); // EventArgs yeniden kullanıldı, burada bir TemperatureEventArgs ama doğru olan HumidityEventArgs olmalı
    }
}

// Olay verilerini taşıyan sınıf (EventArgs türemiş olmalı)
public class TemperatureEventArgs : EventArgs
{
    public int NewTemperature { get; }
    public TemperatureEventArgs(int newTemperature)
    {
        NewTemperature = newTemperature;
    }
}
Dikkat: Standart olay deseni, olay işleyicilerinin ilk parametre olarak object sender (olayı tetikleyen nesne) ve ikinci parametre olarak EventArgs türemiş bir sınıf (olayla ilgili veri taşıyan) almasını gerektirir. Bu, sender/e pattern olarak bilinir ve .NET olayları için bir best practice'dir.

Olaylara Abone Olma ve Abonelikten Çıkma:
Bir olaya abone olmak için `+=` operatörü, abonelikten çıkmak için `-=` operatörü kullanılır.
Kod:
public class Display
{
    public void DisplayTemperature(object sender, TemperatureEventArgs e)
    {
        Console.WriteLine($"Ekran: Yeni sıcaklık algılandı: {e.NewTemperature} derece.");
    }

    public void DisplayHumidity(object sender, TemperatureEventArgs e) // Burda yine TemperatureEventArgs kullandık ama HumidityEventArgs olmalıydı. Örnek kodda düzeltmedik ama gerçekte HumidityEventArgs daha iyi olur.
    {
        Console.WriteLine($"Ekran: Yeni nem oranı algılandı: {e.NewTemperature}% (Sıcaklık EventArgs ile)"); // Burayı sıcaklık yerine nem için uygun şekilde değiştirmeliydik.
    }
}

public class Logger
{
    public void LogTemperatureChange(object sender, TemperatureEventArgs e)
    {
        Console.WriteLine($"Log: Sıcaklık değişimi kaydedildi: {e.NewTemperature} derece. Kaynak: {sender.GetType().Name}");
    }
}

public class MainApp
{
    public static void Main(string[] args)
    {
        Sensor sensor = new Sensor();
        Display display = new Display();
        Logger logger = new Logger();

        // Abone ol
        sensor.TemperatureChanged += display.DisplayTemperature;
        sensor.TemperatureChanged += logger.LogTemperatureChange;
        sensor.HumidityChanged += display.DisplayHumidity; // Nem için abone ol

        Console.WriteLine("Sensör değerleri güncelleniyor...");

        sensor.Temperature = 25; // Olay tetiklenecek
        sensor.Humidity = 60;   // Olay tetiklenecek
        sensor.Temperature = 25; // Aynı değer, olay tetiklenmez

        Console.WriteLine("\nBir abone çıkarılıyor...");
        sensor.TemperatureChanged -= logger.LogTemperatureChange; // Abonelikten çık

        sensor.Temperature = 28; // Sadece DisplayTemperature çağrılacak

        // Abonelikten çıkılmazsa ne olur? Bellek sızıntıları!
        // [quote]
        // Önemli Not: Bir olay abonesi, olay yayıncısı abonelik listesinden kaldırılmazsa,
        // abonelik yapan nesne çöp toplayıcı tarafından toplanmayabilir ve bu durum
        // [b]bellek sızıntılarına (memory leaks)[/b] yol açabilir. Bu nedenle, özellikle uzun ömürlü
        // veya birçok nesnenin birbirine abone olduğu durumlarda,
        // abonelikten çıkmayı unutmamak çok önemlidir.
        // [/quote]
    }
}
event_flow_diagram.png

Yukarıdaki diyagram, bir olayın nasıl tetiklendiğini, abonelerin nasıl bilgilendirildiğini ve genel akışı görselleştirmektedir. (Bu sadece bir örnek görsel linkidir.)

Delegeler ve Olaylar Arasındaki Fark
  • Delege: Temelde bir metod referansıdır. Herhangi bir metodu (imzası uygun olduğu sürece) atayabilir, çağırabilir, listeleyebilir ve hatta listeyi değiştirebilirsiniz. Bir delegeye dışarıdan erişim sağlanabilir ve içerideki abonelik listesi üzerinde tam kontrol vardır.
  • Olay: Bir delege etrafında bir kapsülleyici (wrapper) görevi görür. Temel amacı, bir nesnenin diğer nesnelere bir "olay" hakkında bilgi vermesidir, ancak yayıncının abonelik listesinin dışarıdan manipüle edilmesini engeller. Dışarıdan sadece `+=` (abone ol) ve `-=` (abonelikten çık) işlemleri yapılabilir, delege doğrudan çağırılamaz veya abonelik listesi okunamaz. Bu, olayın güvenliğini ve tutarlılığını sağlar. Yani bir olaya sadece sahibi (yayıncı sınıf) tarafından erişilip tetiklenebilir.
Özetle:
Delegeler, bir veya daha fazla metodu işaret edebilen tür-güvenli "fonksiyon işaretçileridir."
Olaylar, delegeler üzerine inşa edilmiş, gevşek bağlı bildirim mekanizmaları sağlayan özel bir yapıdır. Olaylar, delegelerin güvenli ve kontrollü bir şekilde dışarıya açılmasını sağlar.

Ne Zaman Hangisini Kullanmalı?
  • Delege kullanın:
    • Eğer bir metodu argüman olarak başka bir metota geçirmek istiyorsanız (callback).
    • LINQ sorgularında veya lambda ifadelerinde olduğu gibi kısa, anonim fonksiyonları tanımlamanız gerekiyorsa (`Func`, `Action`, `Predicate`).
    • Bir sınıfın, istemcilerinin doğrudan metodları bir listeye ekleyip çıkarmasını ve listeyi manipüle etmesini istiyorsanız (çok nadir, genellikle olaylar tercih edilir).
  • Olay kullanın:
    • Eğer bir nesne belirli bir durum değişikliği veya eylem hakkında diğer nesneleri bilgilendirmek istiyorsa (tipik olay tabanlı programlama).
    • Eğer yayıncı-abone desenini uygulamak ve yayıncı sınıfının abonelik listesi üzerinde daha fazla kontrol sahibi olmasını sağlamak istiyorsanız (abonelerin doğrudan delegeleri çağırmasını engellemek için).
    • UI programlamasında (düğme tıklamaları, fare hareketleri vb.), veri değişiklik bildirimlerinde veya arka plan işlemlerinin tamamlanmasında.

Gerçek Dünya Senaryoları
  • Kullanıcı Arayüzleri (UI): WPF, Windows Forms gibi çerçevelerde düğme tıklamaları, metin kutusu değişiklikleri gibi olaylar, olay işleyicileri aracılığıyla yönetilir.
  • Asenkron Programlama: Bir ağ isteği tamamlandığında veya bir dosya okuma işlemi bittiğinde bildirim almak için olaylar veya callback delegeler kullanılabilir.
  • Veri Değişikliği Bildirimleri: Bir veri modelindeki bir özellik değiştiğinde, ilgili UI bileşenlerini güncellemek için olaylar kullanılabilir.
  • Plugin Mimarileri: Bir uygulamanın, dışarıdan yüklenen eklentilerin belirli olaylara abone olmasını sağlamak için olaylar mükemmel bir mekanizmadır.
  • Durum Makineleri (State Machines): Bir durumdan diğerine geçiş yapıldığında olaylar tetiklenerek ilgili aksiyonlar alınabilir.

Sonuç
C# delegeleri ve olayları, modern C# programlamanın temel taşlarından ikisidir. Kodunuzu daha modüler, esnek ve yeniden kullanılabilir hale getirmek için vazgeçilmez araçlardır. Delegeler, metod referanslarını soyutlayarak fonksiyonel programlama tarzına yakın yaklaşımlar sunarken; olaylar, gevşek bağlı bir yayıncı-abone iletişim deseni oluşturarak sistem mimarisini iyileştirir. Bu kavramları iyi anlamak ve doğru bir şekilde uygulamak, daha sağlam, bakımı kolay ve ölçeklenebilir C# uygulamaları geliştirmenize yardımcı olacaktır. Unutmayın, özellikle olaylarda abonelikten çıkma (unsubscription) mekanizmasını doğru bir şekilde yönetmek, potansiyel bellek sızıntılarını önlemek adına hayati öneme sahiptir.
Microsoft Docs: Delegates (English)
Microsoft Docs: Events (English)
 
shape1
shape2
shape3
shape4
shape5
shape6
Üst

Bu web sitenin performansı Hazal Host tarafından sağlanmaktadır.

YazilimForum.com.tr internet sitesi, 5651 sayılı Kanun’un 2. maddesinin 1. fıkrasının (m) bendi ve aynı Kanun’un 5. maddesi kapsamında Yer Sağlayıcı konumundadır. Sitede yer alan içerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır.

YazilimForum.com.tr, kullanıcılar tarafından paylaşılan içeriklerin doğruluğunu, güncelliğini veya hukuka uygunluğunu garanti etmez ve içeriklerin kontrolü veya araştırılması ile yükümlü değildir. Kullanıcılar, paylaştıkları içeriklerden tamamen kendileri sorumludur.

Hukuka aykırı içerikleri fark ettiğinizde lütfen bize bildirin: lydexcoding@gmail.com

Sitemiz, kullanıcıların paylaştığı içerik ve bilgileri 6698 sayılı KVKK kapsamında işlemektedir. Kullanıcılar, kişisel verileriyle ilgili haklarını KVKK Politikası sayfasından inceleyebilir.

Sitede yer alan reklamlar veya üçüncü taraf bağlantılar için YazilimForum.com.tr herhangi bir sorumluluk kabul etmez.

Sitemizi kullanarak Forum Kuralları’nı kabul etmiş sayılırsınız.

DMCA.com Protection Status Copyrighted.com Registered & Protected