Event etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
Event etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

C# Lambda Expressions Kullanımı

C# Anonymous Methods (İsimsiz Metodlar) Kullanımı yazımızda isimsiz metodların kullanımından bahsetmiştik. Aslında Lambda Expressions isimsiz metod yaratmanın kısa yoludur.

Lambda Expressions'ı generic List<T> sınıfının FindAll() metodu üzerinden incelemeye çalışalım.
 
     // Method of the System.Collections.Generic.List<T> class.
     public List<T> FindAll(Predicate<T> match)

Yukarıda FindAll metodunun freamwork içerisindeki tanımlamasını görüyorsunuz. Gördüğünüz gibi FindAll Predicate<T> delegelesini parametre alıyor. Predicate<T> delegesi bool döndüren ve bir parametre alan metodları saklayan generic bir parametredir. Freamwork içerisindeki tanımı şu şekildedir:

    // This delegate is used by FindAll() method
    // to extract out the subset.
    public delegate bool Predicate<T>(T obj);

FindAll metodunu çalıştırdığımızda listemizin içerisindeki elemanlar sırasıyla Predicate<T> delegesinin tuttuğu metodlara gidecek ve geriye true yada false değeri dönecektir.

Önce klasik delege tanımı ile bir liste içerisindeki çift sayıları bulan programımızı yazalım:

    class Program
    {
        static void Main(string[] args)
        {
            TraditionalDelegateSyntax();
        }

        static void TraditionalDelegateSyntax()
        {
            List<int> numbers = new List<int>();
            numbers.AddRange(new int[] { 5, 7, 9, 14, 17, 2, 6 });

            //klasik delege tanimi ile
            Predicate<int> predicate = new Predicate<int>(IsEvenNumber);
            List<int> evenNumbers = numbers.FindAll(predicate);

            foreach (int item in evenNumbers)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }

        // Target for the Predicate<> delegate.
        static bool IsEvenNumber(int i)
        {
            // Is it an even number?
            return (i % 2) == 0;
        }
    }

Gördüğünüz gibi önce Predicate<int> tipinde bir delege nesnesi tanımladık. Ardından IsEvenNumber metodunu bu delegenin saklayacağı metod olarak parametre verdik. Ardından FindAll metodu ile bu delegenin taşıdığı IsEvenNumber metodunu number listemizin her adımı için çalıştırdık. Klasik yöntem ile bu şekilde FindAll metodunu çalıştırabiliriz. Şimdi birde aynı örneği Anonymous Methods ile yazalım:

        static void AnonymousMethodsDelegateSyntax()
        {
            List<int> numbers = new List<int>();
            numbers.AddRange(new int[] { 5, 7, 9, 14, 17, 2, 6 });

            //Anonymous Methods ile
            List<int> evenNumbers = numbers.FindAll(
                delegate(int i){ return (i % 2) == 0; }
                );

            foreach (int item in evenNumbers)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }

Bu şekilde isimsiz metodlar ile çift sayı bulma işlemini gerçekleştirmiş olduk. İsimsiz metodlar ile ilgili detaylı bilgiye şu yazıdan ulaşabilirsiniz: C# Anonymous Methods (İsimsiz Metodlar) Kullanımı 

Son olarak Lambda Expressions ile yazımına bakalım:

        static void LambdaExpressionsSyntax()
        {
            List<int> numbers = new List<int>();
            numbers.AddRange(new int[] { 5, 7, 9, 14, 17, 2, 6 });

            //Lambda Expressions ile
            List<int> evenNumbers = numbers.FindAll(i => i % 2 == 0);

            foreach (int item in evenNumbers)
            {
                Console.WriteLine(item);
            }

            Console.ReadLine();
        }

Gördüğünüz gibi Lambda Expressions ile yazımı çok basit bir hale geldik. Compiler Lambda Expressions gördüğünde o satırı Anonymous Methods'a çevirir ve öyle yorumlar. Örneğin yukarıdaki örneği ildasm exe ile açarsanız Lambda Expressions içeren satırının Anonymous Methods'a çevrildiğini görürsünüz:

      List<int> evenNumbers = list.FindAll(delegate (int i)
      {
          return (i % 2) == 0;
      });

Lambda Expressions ile yazdığımız kod şu an sadece return işlemi yapıyor. Fakat biz birden fazla işlemi burada yaptırabiliriz:

      List<int> evenNumbers = list.FindAll((i) =>
      {
          Console.WriteLine("value of i is currently: {0}", i);
          bool isEven = ((i % 2) == 0);
          return isEven;
      });

Eğer hiç bir parametre gönderilmiyor ise metoda şu şekilde Lambda Expressions yazılır:

       VerySimpleDelegate d = new VerySimpleDelegate( 
       () => {return "Enjoy your string!";} );

Eğer birden fazla parametre gönderiliyor ise:

       VerySimpleDelegate d = new VerySimpleDelegate( 
       (msg, result) => 
         {Console.WriteLine("Message: {0}, Result: {1}", msg, result);} );

Çıktı:



Örnek Proje:

LambdaExpressionsExample.rar

C# Anonymous Methods (İsimsiz Metodlar) Kullanımı

Ö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. Bu yazılarımızda delege tanımlarını daha önce tanımlanmış olduğumuz named metodlar ile yaptık. C# 2.0 ile birlikte bunu isimsiz metodlar ile yapabiliyor hale geldik.

button1.Click += delegate
{
    MessageBox.Show("isimsiz metodumuz calisti");
};

Önce daha önceki yazılarımızda named metodlar ile yazdığımız delege örneğimizi hatırlayalı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);
        }
    }

Şimdi bunu anonymous methodlar ile nasıl yaparız ona bakalı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" };

            //ÖNEMLİ!! ShowChangedUser yerine isimsiz metod kullandik
            p1.PersonHasChanged += delegate(Person p, string fieldName
            , string newValue)
            {
                Console.WriteLine(@"Person ID: {0} Field: {1}   
                    New Value: {2}", p.PersonId, fieldName, newValue);
            };

            //ÖNEMLİ!! SendChangedUserInformation yerine isimsiz metod kullandik
            p1.PersonHasChanged += delegate(Person p, string fieldName
            , string newValue)
            {
                Console.WriteLine(@"Person ID: {0} Bilgilendirme maili   
                    gonderiliyor", p.PersonId, fieldName, newValue);
            };

            //ÖNEMLİ!! parametresiz isimsiz metodda kullanabiliriz
            p1.PersonHasChanged += delegate
            {
                Console.WriteLine("Degisiklik calisti.");
            };

            p1.Name = "Test";

            Console.ReadLine();

        }
    }

Gördüğünüz gibi artık ShowChangedUser ve  SendChangedUserInformation named metodlarını kullanmadık onun yerine isimsiz metodlar ile delege tanımlarımızı gerçekleştirdik.

Burda önemli noktalardan biri en son metod atama işleminde parametresiz metod ataması yapabildik. Named metod kullansaydık buna izin verilmiyor olurdu. Çünkü named metod kullanımında metodun parametreleri ile delege tanımındaki parametrelerin uyuşması gerekmektedir.

Çıktı:




İsimsiz metodlar kod aşamasında isimsiz gözüksede derlenme aşamasında, unique olarak isimlendirilmiş bir metoda dönüştürülür. ildasm ile yazdığınız koda bakarsanız bunu açıkça görebilirsiniz.

Anonymous Methodlar:

* Anonymous methodlar, delegate sözcüğüyle başlamak zorundadır.
* Herhangi bir türde parametre kabul ettiklerini ederler. ref ve out sözcükleriyle birlikte de kullanılabilir.
* params sözcüğü, anonymous metodlarda kullanılamaz.
* Tanımlandığı sınıf içerisindeki herhangi bir değişken ile aynı isme sahip değişkeni olamaz.
* Bir metoda parametre olarak gönderilebilir. Örnek:

        int[] CiftSayilar = Array.FindAll(Sayilar, delegate(int Sayi)
        {
           return (Sayi % 2 == 0);
        });

Örnek Proje:
AnonymousMethodsExample.rar

C# Event Kullanımı II - Generic Events

C# Event Kullanımı yazımızda event kavramına giriş yapmıştık. Bu yazımızda generic event kavramını incelemeye çalışacağız.

EventArgs sınıfından türeyen kendi custon EventArgs sınıfımızı yazabiliriz:

    public class CustomEventArgs : EventArgs
    {
        public int PersonId { get; set; }
        public string FieldName { get; set; }
        public string NewValue { get; set; }
    }


CustomEventArgs sınıfımızı yazdıktan sonra artık delege tanımı yapmadan freamwork içerisinde event handle etmek için kullanılan EventHandle<> generic sınıfını kullanarak programımızı yazabiliriz.

    class Person
    {
        public event EventHandler<CustomEventArgs> 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)
                {
                    CustomEventArgs c = new CustomEventArgs();
                    c.PersonId = this.PersonId;
                    c.FieldName = "Name";
                    c.NewValue = value;
                    PersonHasChanged(this, c);
                }
            }
        }

    }
    public class CustomEventArgs : EventArgs
    {
        public int PersonId { get; set; }
        public string FieldName { get; set; }
        public string NewValue { get; set; }
    }

    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(object sender, CustomEventArgs e)
        {
            Console.WriteLine(@"Person ID: {0} Field: {1} 
                    New Value: {2}", e.PersonId, e.FieldName, e.NewValue);
        }

        static void SendChangedUserInformation(object sender
                , CustomEventArgs e)
        {
            Console.WriteLine(@"Person ID: {0} Bilgilendirme maili 
                    gonderiliyor", e.PersonId, e.FieldName, e.NewValue);
        }
    }

Çıktı:


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