C# Event Kullanımı I - Event Nedir?

Önceki C# Delegate Kullanımı I - Delege Nedir? ve C# Delegate Kullanımı II - Generic Delegeler yazılarımızda delege kavramından bahsetmiştik. Kısacası delegeleri metot saklamak için kullandığımızı hatırlayalım.

Bu yazıları okuduysanız ya da delegeler hakkında bilginiz var ise programlarımızda delege kullanacaksak bir çok tanımlama yapmamız gerekiyor: delege tanımlama, encapsule için custom register ve unregister metotları gibi.

Şimdi düşünelim ki Person isminde bir sınıfımız var ve biz Person nesnesi üzerinde bir değişiklik yapılınca bir takım metodların çalışmasını istiyoruz. Bu durumda şöyle bir tasarım gerçekleştirebiliriz: bir delege tanımlarız ardından bu delegenin tutacağı metodları setlemek için bir metod yazarız ve bu sayede bir değişiklik olduğunda istediğimiz metodların çalışmasını sağlarız. Gördüğünüz gibi event kullanmadan tasarımı gerçekleştirdik. Şimdi bunu koda dökelim:

    public class Person
    {
        public delegate void PersonChangeHandler(Person person
           , string fieldName
           , string newValue);
        private PersonChangeHandler PersonHasChanged;

        public void RegisterMethod(PersonChangeHandler methodToAdd)
        {
            PersonHasChanged += methodToAdd;
        }

        public void UnregisterRegisterMethod(PersonChangeHandler methodToAdd)
        {
            PersonHasChanged -= methodToAdd;
        }

        public int PersonId { get; set; }
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                //eger method set edildi ise
                if(PersonHasChanged != null)
                    PersonHasChanged(this, "Name", value);
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person() { PersonId = 1, Name = "Safak" };
            p1.RegisterMethods(ShowChangedUser);
            p1.RegisterMethods(SendChangedUserInformation);
            p1.Name = "Test";

            Console.ReadLine();
        }

        static void ShowChangedUser(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"\nPerson ID: {0} Field: {1} 
                    New Value: {2}", p.PersonId, fieldName, newValue);
        }

        static void SendChangedUserInformation(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"Person ID: {0} Bilgilendirme maili 
                    gonderiliyor", p.PersonId, fieldName, newValue);
        }
    }

Çıktı:



Gördüğünüz gibi PersonChangeHandler isminde bir delege tanımladık. Daha sonra Person sınıfımızın içerisine PersonHasChanged isminde delege nesne örneği tanımladık. Ardından custom register ve unregister metodlarımızı yazdık. Burada PersonHasChanged delege nesne örneğinin private tanımlanması önemli çünkü bununla encapsule işlemini gerçekleştirmiş olduk. Client delege nesne örneğine direk ulaşamıyor ancak register ve unregister metodları ile delegeye yeni metodlar ekleyip çıkarabiliyor.

Peki PersonHasChanged public olsaydı ne olurdu? Bu durumda artık custom register ve unregister metodları yazmamıza gerek kalmazdı. Yeni programımız şu şekilde olurdu:

    public class Person
    {
        public delegate void PersonChangeHandler(Person person
           , string fieldName
           , string newValue);
        public PersonChangeHandler PersonHasChanged;

        public int PersonId { get; set; }
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                //eger method set edildi ise
                if(PersonHasChanged != null)
                    PersonHasChanged(this, "Name", value);
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person() { PersonId = 1, Name = "Safak" };
            p1.PersonHasChanged += ShowChangedUser;
            p1.PersonHasChanged += SendChangedUserInformation;
            p1.Name = "Test";

            Console.ReadLine();
        }

        static void ShowChangedUser(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"\nPerson ID: {0} Field: {1} 
                    New Value: {2}", p.PersonId, fieldName, newValue);
        }

        static void SendChangedUserInformation(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"Person ID: {0} Bilgilendirme maili 
                    gonderiliyor", p.PersonId, fieldName, newValue);
        }
    }

Bu şekilde de programımız çalışacak ve aynı sonucu üretecektir. Fakat burada bir sorun var. Encapsule işlemi gerçekleştirmedik. Client direk olarak delege nesnesine ulaşabiliyor. Bu sayede artık clientin istediği gibi delegenin Invoke metodunu çalıştırma özgürlüğü var. Örneğin aşağıdaki kodu artık yazabilir:

            p1.PersonHasChanged.Invoke(p1, "hebele", "hübele");

Bu durumda çıktı:



Gördüğünüz gibi client gitti p1 nesnemizin sanki hebele diye bir alanı varmış gibi bunun değiştiğini belirtti ve metodların çalışmasına neden oldu. Bu önemli bir güvenlik açığı oldu programımız için. Peki ne yapabiliriz?

Event kavramı tam burada ihtiyacımıza koşuyor. Eventlar, delegelerin encapsule edilmiş halidir. PersonHasChanged delege örneğini event olarak tanımlasaydık hem custom register ve unregister metodlarını yazmaktan kurtulurduk hem de yukarıda anlattığımız public encapsule problemini yaşamazdık.

Şimdi programımızı birde event ile yazalım:

public class Person
    {
        public delegate void PersonChangeHandler(Person person
                    , string fieldName, string newValue);
        public event PersonChangeHandler PersonHasChanged;

        public int PersonId { get; set; }
        private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                //eger method set edildi ise
                if (PersonHasChanged != null)
                    PersonHasChanged(this, "Name", value);
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Person p1 = new Person() { PersonId = 1, Name = "Safak" };
            p1.PersonHasChanged += ShowChangedUser;
            p1.PersonHasChanged += SendChangedUserInformation;
            p1.Name = "Test";

            Console.ReadLine();

        }

        static void ShowChangedUser(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"Person ID: {0} Field: {1} 
                    New Value: {2}", p.PersonId, fieldName, newValue);
        }

        static void SendChangedUserInformation(Person p, string fieldName
            , string newValue)
        {
            Console.WriteLine(@"Person ID: {0} Bilgilendirme maili 
                    gonderiliyor", p.PersonId, fieldName, newValue);
        }
    }

Gördüğünüz gibi hem custom register ve unregister işlemlerinden kurtulduk hem de bu sayede event ile delegemiz encapsule olduğu için client Invoke metodunu kafasına göre çalıştıramadı.

p1.PersonHasChanged'ın sonuna nokta koyup metot listesine erişmeye çalışırsanız hiç bir metodun çıkmadığını görürsünüz.

C# windows form uygulaması geliştirdiyseniz burada eventların bolca kullanıldığını görürsünüz. Buton click event'ı gibi bir çok event bulunmakta. Konuyu daha iyi anlamak için bu eventları incelemenizi öneririm.

Örnek Projeler:
PublicDelegateProblem.rar
EventDelegateExample.rar




0 yorum :

Yorum Gönder