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

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# Delegate Kullanımı II - Generic Delegeler

C# Delegate Kullanımı I - Delege Nedir? yazımızda delege kavramını, ne işe yaradığını, nerelerde kullanıldığını incelemiştik.

C# generic delege tanımlama yapmamıza olanak sağlar. Örneğin, void döndüren ve bir parametre alan 2 metodumuz olsun: StringTarget; string tipinde parametre alıyor ve geriye void döndürüyor. IntTarget ise int tipinde parametre alıyor ve o da geriye void döndürüyor.

Normal şartlarda 2 delege tanımlamamız birine string parametresi diğerine int parametresi yazmamız gerekir. Şimdi ilk aklımıza gelen şekilde bu programı yazalım:

        delegate void MyStringDelegate(string args);
        delegate void MyIntDelegate(int args);

        static void Main(string[] args)
        {
            MyStringDelegate stringDelegate = new MyStringDelegate(StringTarget);
            MyIntDelegate intDelegate = new MyIntDelegate(IntTarget);

            stringDelegate("Safak");
            intDelegate(26);

            Console.ReadLine();
        }

        static void StringTarget(string arg)
        {
            Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
        }
        static void IntTarget(int arg)
        {
            Console.WriteLine("++arg is: {0}", ++arg);
        }

Gördüğünüz gibi string tipinde parametre alan metodumuz için MyStringDelegate, int tipinde parametre alan metodumuz için ise MyIntDelegate delegesini tanımladık. Peki yine void döndüren fakat parametre olarak double alan bir metodu delege etmek istesek ne yapacağız? Bu durumda yeni bir MyDoubleDelegate yazmamız gerekecek. Bu durumu ortadan kaldırmak için şimdi bu programı birde generic delege kullanarak yazalım:

 
        //generic delegate
        delegate void MyGenericDelegate<T>(T args);

        static void Main(string[] args)
        {
            //generic delegate
            MyGenericDelegate<string> stringDelegate 
                = new MyGenericDelegate<string>(StringTarget);
            MyGenericDelegate<int> intDelegate 
                = new MyGenericDelegate<int>(IntTarget);

            stringDelegate("Safak");
            intDelegate(26);

            Console.ReadLine();
        }

        static void StringTarget(string arg)
        {
            Console.WriteLine("arg in uppercase is: {0}", arg.ToUpper());
        }
        static void IntTarget(int arg)
        {
            Console.WriteLine("++arg is: {0}", ++arg);
        }

Gördüğünüz gibi delegate void MyGenericDelegate<T>(T args); generic delege tanımı yaparak Hem StringTarget hemde IntTarget metodlarını saklayabildik. Bundan sonra double paramere alan geriye void döndüren bir fonksiyon yazsakta yeni bir delege tanımı yapmamıza gerek kalmayacak, MyGenericDelegate üzerinden nesne örneği oluşturup T yerine double vererek bunu gerçekleştirebiliriz.

Çıktı:



Action<> ve Func<> Kullanımı:

Normal şartlarda delege kullanmamız için şu adımları gerçekleştirmemiz gerekiyor:
* İçinde saklayacağı metot formatına uygun custom delege tanımı yapmak
* Bu custom delegeden bir nesne örneği oluşturmak
* Oluşturduğumuz nesne üzerinden Invoke işlemini gerçekleştirmek
Action<> ve Func<> kısa bir şekilde delege tanımı yapmamıza olanak sağlıyor.

Action<> Kullanımı:

Geriye void döndüren metodları tutan delegeleri kısa yoldan tanımlamamızı sağlar.

        static void Main(string[] args)
        {
            Action<string, int> action
                      = new Action<string, int>(DisplayAge);
            action("Safak", 26);
        }

        static void DisplayAge(string fullName, int age)
        {
            Console.WriteLine(fullName + " : " + age);
        }

Gördüğünüz gibi delege tanımı yapmadan DisplayAge metodu saklandı ve Invoke edilebildi. Bir kez daha söylemekte fayda var Action<> geri değer döndürmez.

Func<> Kullanımı:

Action<> 'ın geri değer döndürebilen halidir. Son parametre dönüş değer tipini belirtir. Örnek kullanım:

        static void Main(string[] args)
        {
            Func<DateTime, double> func 
                 = new Func<DateTime, double>(GetUserAgeAtTime);
            double dateDif = func(DateTime.Now.AddYears(-2));
            Console.WriteLine("\nDate Dif: {0}", dateDif);

            Console.ReadLine();
        }

        static double GetUserAgeAtTime(DateTime birthDate)
        {
            return (DateTime.Now - birthDate).TotalDays;
        }

Yukarıdaki örnekte <DateTime, double> şeklinde yazılmıştır. DateTime parametrenin tipini double ise dönüş değer tipini belirtir.

Örnek ProjelerGenericDelegateExample.rar ,  ActionFuncExample.rar

C# Generic ObservableCollection<T> Kullanımı

ObservableCollection<T> yapımız System.Collections.ObjectModel namespace'i altında bulunuyor. İçerisinde aynen List<T> gibi T ile belirttiğimiz tipte elemanlar tutar. List<T> collection yapısından en önemli farkı ise içerisinde CollectionChanged isminde bir event barındırmasıdır. Collection nesnemiz değiştiğinde bu event fire olur ve biz bu sayede collection nesnemiz üzerindeki değişikliklerden haberdar oluruz.

Şimdi örnek bir program ile collection yapımızı inceleyelim:

 public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }

        public override string ToString()
        {
            return "Fullname: " + Name + " " + Surname + " Age: " + Age;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ObservableCollection<Person> people 
                = new ObservableCollection<Person>();

            // event'i delege ediyoruz
            people.CollectionChanged += people_CollectionChanged;

            people.Add(new Person() 
                { Name = "Salih", Surname = "Sayar", Age = 42 });
            people.Add(new Person() 
                { Name = "Ahmet", Surname = "Yilmaz", Age = 21 });

            people.RemoveAt(0);

            Console.ReadLine();
        }

        private static void people_CollectionChanged(object sender,
  System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            // Ne nedenle bu event calisti?
            Console.WriteLine("nAction for this event: {0}", e.Action);

            // Silindi ise
            if (e.Action == 
   System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            {
                Console.WriteLine("Silinen insanlar:");
                foreach (Person p in e.OldItems)
                {
                    Console.WriteLine(p.ToString());
                }
                Console.WriteLine();
            }

            // eklendi ise
            if (e.Action == 
    System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            {
                Console.WriteLine("Eklenen insanlar:");
                foreach (Person p in e.NewItems)
                {
                    Console.WriteLine(p.ToString());
                }
            }
        }
    }

Çıktı:



Örnek Proje: ObservableCollectionExample.rar

C# Generic SortedSet<T> Kullanımı

SortedSet<T> yapısı C#'ın generic collection yapılarından biridir. T ile belirttiğimiz tipte elemanlar tutar. C# 4.0 ile hayatımıza girmiş yapılardan biridir. Bu yapının en önemli özelliği listeyi sıralı tutmasıdır. Siz yeni bir eleman eklesenizde silsenizde liste yine sıralı olacaktır.

SortedSet ile int, string gibi tipler tutacaksak bu tipler IComparer interface'ini implemente ettiği için ekstra hiç bir şey yapmamıza gerek yoktur. Bu elemenları SortedSet'e ekleriz o arkada bunları sıralar. Peki Person isminde bir sınıf tutacaksak bunu nasıl sıralıyacak?

Bunun için kendi Comparer sınıfımızı yazmamış gerekir. Şimdi gelin bu durumu kod üzerinde inceleyelim:

 public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }

        public override string ToString()
        {
            return "Fullname: " + Name + " " + Surname + " Age: " + Age;
        }
    }

    public class PersonComparer : IComparer<Person>
    {
        public int Compare(Person x, Person y)
        {
            // 1: siralamada daha onde
            //-1: siralamada daha geride
            // 0: ayni sirada
            if (x.Age > y.Age)
                return 1;
            else if (x.Age < y.Age)
                return -1;
            else
                return 0;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            //PersonComparer sinifi ile liste yasa gore sirali
            //hale getiriliyor.
            SortedSet<Person> personSortedSet 
                = new SortedSet<Person>(new PersonComparer());

            //SortedSet yapisina Person nesnelerini ekliyoruz
            personSortedSet.Add(new Person() 
                { Name = "Salih", Surname = "Sayar", Age = 42 });
            personSortedSet.Add(new Person() 
                { Name = "Ahmet", Surname = "Yilmaz", Age = 21 });
            personSortedSet.Add(new Person() 
                { Name = "Safak", Surname = "Unel", Age = 26 });

            // 3 person yasina gore sirali ekrana yaziliyor
            foreach (Person p in personSortedSet)
            {
                Console.WriteLine(p);
            }
            Console.WriteLine();

            // 2 kisi daha ekleniyor
            personSortedSet.Add(new Person() 
                { Name = "Sunay", Surname = "Tarik", Age = 23 });
            personSortedSet.Add(new Person() 
                { Name = "Eren", Surname = "Sahan", Age = 34 });

            // yeni eklenen 2 person nesnesi sirali sekilde
            // eklenecektir.
            foreach (Person p in personSortedSet)
            {
                Console.WriteLine(p);
            }

            Console.ReadLine();
        }
    }

Çıktı:


Örnek Proje: GenericSortedSetExample.rar

C# Generic Queue<T> Kullanımı

Queue<T> yapısı C#'ın generic collection yapılarından biridir. T ile belirttiğimiz tipte elemanlar tutar ve FIFO (ilk giren ilk çıkar) mantığıyla elemanları dışarı çıkartır.

Bu yapıya örnek olarak bankaya gittiğimizde girdiğimiz sırayı verebiliriz. İlk bankaya gidene ilk sıra gelir gibi.

Enqueue : Queue yapımıza yeni bir eleman eklemek için kullanılır.
Dequeue : Queue yapımızdan sıradaki ilk elemanı alıp bu elemanı siler.
Peek : Queue'deki nesneyi silmeden son elemanı döndürür.

Şimdi örnek bir Queue yapısı geliştirelim:

 public class Person
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public int Age { get; set; }

        public override string ToString()
        {
            return "Fullname: " + Name + " " + Surname + " Age: " + Age;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Queue<Person> personQueue = new Queue<Person>();

            //Queue yapisina Person nesnelerini ekliyoruz
            personQueue.Enqueue(new Person() 
                 { Name = "Ahmet", Surname = "Yilmaz", Age = 21 });
            personQueue.Enqueue(new Person() 
                 { Name = "Safak", Surname = "Unel", Age = 26 });
            personQueue.Enqueue(new Person() 
                 { Name = "Salih", Surname = "Sayar", Age = 42 });

            //Peeked: son eleman silmeden queue'deki son elemana bakiliyor
            //Dequeue: ilk eleman cekilip siliniyor  
            Console.WriteLine("Peeked : {0}", personQueue.Peek());
            Console.WriteLine("Dequeue : {0}", personQueue.Dequeue());

            Console.WriteLine("nPeeked : {0}", personQueue.Peek());
            Console.WriteLine("Dequeue : {0}", personQueue.Dequeue());

            Console.WriteLine("nPeeked : {0}", personQueue.Peek());
            Console.WriteLine("Dequeue : {0}", personQueue.Dequeue());

            try
            {
                // eger stack'imiz bosalttiktan sora Peek veya Pop  
                // metodlarimizi cagirirsak hata aliriz  
                Console.WriteLine("nPeeked : {0}", personQueue.Peek());
                Console.WriteLine("Dequeue: {0}", personQueue.Dequeue());
            }
            catch (Exception e)
            {
                Console.WriteLine("nError: {0}", e.Message);
            }

            Console.ReadLine();  

        }
    }

Çıktı:



Örnek Proje: GenericQueueExample.rar

C# Generic Stack<T> Kullanımı

Stack<T> yapısı C#'ın generic collection yapılarından biridir. T ile belirttiğimiz tipte elemanlar tutar ve LIFO (Son giren ilk çıkar) mantığıyla elemanları dışarı çıkartır.

Push : Collection nesnemize yeni eleman ekler.
Pop : Collection'a son giren elemanı çekip bu elemanı collection'dan siler.
Peek : Collection'daki nesneyi silmeden son elemanı döndürür.

Şimdi örnek bir stack yapısı geliştirelim:

       public class Person
       {
           public string Name { get; set; }
           public string Surname { get; set; }
           public int Age { get; set; }

           public override string ToString()
           {
              return Name + " " + Surname + " Age: " + Age;
           }
        }
        static void Main(string[] args)
        {
            Stack<Person> personStack = new Stack<Person>();

            personStack.Push(new Person() 
                 { Name = "Ahmet", Surname = "Yilmaz", Age = 21 });
            personStack.Push(new Person() 
                 { Name = "Safak", Surname = "Unel", Age = 26 });
            personStack.Push(new Person() 
                 { Name = "Salih", Surname = "Sayar", Age = 42 });

            //Peeked: son eleman silmeden stack'teki son elemana bakiliyor
            //Popped: son eleman cekilip siliniyor
            Console.WriteLine("Peeked : {0}", personStack.Peek());
            Console.WriteLine("Popped : {0}", personStack.Pop());

            Console.WriteLine("\nPeeked : {0}", personStack.Peek());
            Console.WriteLine("Popped: {0}", personStack.Pop());

            Console.WriteLine("\nPeeked : {0}", personStack.Peek());
            Console.WriteLine("Popped: {0}", personStack.Pop());

            try
            {
                // eger stack'imiz bosalttiktan sora Peek veya Pop
                // metodlarimizi cagirirsak hata aliriz
                Console.WriteLine("\nPeeked : {0}", personStack.Peek());
                Console.WriteLine("Popped: {0}", personStack.Pop());
            }
            catch (Exception e)
            {
                Console.WriteLine("\nError: {0}", e.Message);
            }

            Console.ReadLine();
        }
    }

Çıktı:



Örnek Proje : GenericStackExample.rar

C# Generic Liste-Sınıf Yapıları

Soldaki kupa fotografını generic konusunu araştırırken buldum çok hoşuma gittiği için sizle paylaşmak istedim. Aslında fotograf tüm konuyu özetliyor T kupamızın içerisine koyacağımız çay, kahve, su gibi değişkenleri simgeliyor. Bizde T'ye bunlardan birini verip kupamızı dolduruyoruz. Yazının soununda fotografa dönün sizde çok seveceksiniz:)

Asıl konumuza dönersek neden generic sınıf ve listelere ihtiyaç duyuyoruz bunu anlamak için önce non-generic üyeler kullanarak bir kaç örnek yapalım bakalım ne takım problemler ortaya çıkacak görelim.

Önce hepimizin defalarca kullandığı dizileri düşünelim: Dizi tanımı yapılırken dizi hangi tipte değerler taşıyacak ise bunu belirtmemiz gerekiyor; dizimiz string tipinde değerler tutacak ise string, int tipinde değerler tutacaksa int tipinde dizi oluşturulur. Daha sonrada dizimize boyut vermemiz gerekir. Bunları örnek bir string dizisi tanımlayarak gösterelim:

static void Main(string[] args)
{
   // string tipinde 3 elemanli bir dizi olusturalim
   string[] strArray = {"Ahmet", "Mehmet", "Ali" };
}

 Gördüğünüz gibi dizimizin boyutunu 3 verdik bu yüzden bu dizi artık 3 değer tutar 4. değeri artık bu diziye atayamayız. Atarsak hata alırız. Peki bizim ne kadarlık bir diziye ihtiyaç duyduğumuz önceden belli değilse ne olacak? Ya çok büyük bir dizi tanımlayacağız ama bu da belekte boşa yer kaplayacaktır ya da tahmin ettiğimizden daha fazla kayıt gelince programımız patlayacaktır.

Bu durumu ortadan kaldırmak için System.Collection namespace'i altında bulunan ArrayList gibi collection sınıflarını kullanabiliriz. Bu collection'lara önceden boyut belirtmek zorunda değiliz. Add metodu ile istediğimiz sayıda obje saklayabiliriz.

static void Main(string[] args)
{
    ArrayList strArray = new ArrayList();
    strArray.Add("Ahmet");
    strArray.Add("Mehmet");
    strArray.Add("Ali");
    strArray.Add("Ayşe");
}

Gördüğünüz gibi bir boyut vermeden istediğimiz sayıda obje gönderebildik. Peki aynı ArrayList'e tüm gönderdiğimiz değerler string tipinde olmasına rağmen 5. obje olarak int tipinde bir obje gönderebilir miyiz?
  strArray.Add(1);
İşte aslında neden generic listelere ihtiyaç duyuyoruz bu sorunun cevabında gizli. Evet gönderebiliriz. Çünkü ArrayList object tipinde nesneler tutar. string, int, class tüm bunlar .net dünyasında bir object olduğu için ArrayList'te hepsi aynı anda tutulabilir. Peki bunun dezavantajları nelerdir? Listeleyelim:

* ArrayList'e bir eleman eklediğimizde bu eleman object tipinde değilse ArrayList object tipinde elemanlar tuttuğu için arkaplanda önce bu eleman objeye çevrilir öyle ArayList'e eklenir. Bu performans sorununa neden olacaktır. 
* Tersi içinde aynı durum söz konusudur. ArrayList'ten bir eleman çektiğimizde object tipinde bize dönecektir. Fakat biz bu elamanı int tipinde istiyorsak bunu int'e çevirmemiz gerekecektir. Aynı şekilde buda performans sorununa neden olacaktır. 
* foreach ile ArrayList'imiz üzerinde döndüğümüzü düşünelim bize gelen her nesne object olduğu için gerçek halinin ne olduğunu bilemeyiz. Gelen her elemanı int tipine çevirsek belki arada bir string tipinde "Ayşe" gelecektir bu durumda programımız hata alacaktır. Yani non-generic koleksiyonlar type-safe değildir.

Peki bu tip durumlardan kurtulmak için ne kullanmamız gerekiyor: Generic List<T>

// Bu List<T> artık sadece int tipinde elemanlar tutar
List intListesi = new List();
intListesi.Add(10);
intListesi.Add(2);

Bu durumda artık listeye int tipinde bir eleman eklenirken arka planda obje tipine çevrilmesine gerek kalmadı. Aynı şekilde generic listemizden bir eleman çektiğimizde bunun int tipinde olacağından eminiz, fazladan bir convert işlemi yapmamızda gerek kalmadı. Ayrıca dönecek elemanın int tipinde olacağı kesin olduğu için type-safe bir yapıya sahiptir.

Bunun gibi List<T>Stack<T>Queue<T>SortedSet<T> gibi generic liste yapıları mevcuttur bu yapılarla ilgili örnekleri ilerleyen yazılarımızda paylaşacağız. Ayrıca ilginç bir collection yapısı olan ObservableCollection<T>'da mevcuttur incelemenizi öneririm.

Bunların dışında .Net Freamwork'ü içerisinde bir çok generic interface, sınıf , metot ve liste bulunmaktadır. Bizde kendi generic interfacesınıf ve metodlarımızı yazabiliriz. Örnek bir metot yazarak bunu açıklayalım:

//int tipinde Swap
static void Swap(ref int a, ref int b)
{
   int temp;
   temp = a;
   a = b;
   b = temp;
}

Görüldüğü gibi gönderilen 2 int değerini birbirine atayan basit bir Swap metodu yazdık. Fakat düşünün ki daha sonra string tipindeki 2 elemanı birbirine atayacak bir metoda daha ihtiyaç duyduk. Bu durumda bir metot daha yazmamız gerekecek:

//string tipinde Swap
static void Swap(ref string a, ref string b)
{
   string temp;
   temp = a;
   a = b;
   b = temp;
}

Bunun gibi oluşturduğumuz Person sınıfındaki 2 nesneyi birbirine atamamız gerekebilir. Bu tip durumlarda sınıfımıza hep yeni bir metot yazmak zorunda kalıcaz. Bunun yerine generic bir metot yazabiliriz:

static void Swap<T>(ref T a, ref T b)
{
   T temp;
   temp = a;
   a = b;
   b = temp;
}

Şu şekilde artık tek metot üzerinden Swap işlemimizi yapabiliriz:

//int ile Swap
int a = 1;
int b = 2;
Swap<int>(ref a, ref b);

//string ile Swap
string s1 = "Ahmet";
string s2 = "Mehmet";
Swap<string>(ref s1, ref s2);

Şimdi birde generic bir sınıf tasarlayalım:

        public class Point<T>
        {
            // noktamizin x,y koordinatlari
            private T xPos;
            private T yPos;
            // constructor.
            public Point(T xVal, T yVal)
            {
                xPos = xVal;
                yPos = yVal;
            }
            // Generic properties.
            public T X
            {
                get { return xPos; }
                set { xPos = value; }
            }
            public T Y
            {
                get { return yPos; }
                set { yPos = value; }
            }
            public void ResetPoint()
            {
                //default T tipinin default degerini dondurur
                //T int olursa 0, string olursa null gibi
                X = default(T);
                Y = default(T);
            }
        }

Gördüğünüz gibi Point sınıfı tanımladık ve bu Point sınıfının içerisine ise X,Y koordinatları tanımladık. Fakat generic bir sınıf tanımı yaptığımız için bu koordinatların tipini önceden tanımlamadık. Bu sınıfı kullanan client isterse int tipinden Point nesnesi oluşuturur bu durumda X,Y kordinatları int tipinden olur; isterse de double tipinden oluşturur bu durumda da X,Y koordinatları double tipinden olur.

    // int ile Point
    Point<int> p = new Point<int>(10, 10);
    // double ile Point
    Point<double> p2 = new Point<double>(5.4, 3.3);

Burada default keywordu dikkatinizi çekmiş olabilir: default keyword'u kod commenti olarak yazdığımız gibi tipin default değerini döndürür. int için 0, string için null gibi.

where T : <ifade> Kullanımı:

Generic yapısını kurarken T için bazı kısıtlamalar koyabiliriz. Bunun için "where T : <ifade>" yapısı kullanılır:

where T : struct  = T'nin System.ValueType tipinde olması gerektiğini belirtmek için kullanılır.
where T : class   = T'nin System.ValueType tipinde olmaması gerektiğini belirtmek için kullanılır.
where T : new()  = T'nin default constructor'ının olması gerektiğini belirtmek için kullanılır.
where T : NameOfBaseClass = NameOfBaseClass yerine bir sınıf ismi yazılır ve T'nin bundan türemiş olması gerektiğini belirtmek için kullanılır.
where T : NameOfInterface = NameOfInterface yerine interface ismi yazılır ve T'nin bu interface'i veya interfaceleri implemente etmiş olması gerekir. Birden fazla interface yazılabilir.
Örnek kullanımına bakarsak:

public class MyGenericClass<T> where T : class, IDrawable, new()

Umarım yararlı olmuştur. Görüşmek üzere.