.Net platformu ile yapılan uygulamaları incelediğimiz takdirde, tamamına yakınının veri odaklı olduğunu söylemek mümkündür. Genellikle uygulamalarda var olan veri kaynaklarından , işimize yarayanları çekip işleyerek kullanıcıya anlamlı hale getirmeye çalışırız. Bu noktada SQL tarzı sorgular aklımıza gelmektedir. Genel olarak düşünürsek, veriyi çekmek için select, filtrelemek için where gibi ifadeler kullanırız. C# 3.0 ile gelen yeniliklerin temel amacı LINQ mimarisine alt yapı oluşturmaktır. LINQ mimarisi de , veri tabanında yapılabilen SQL sorgularının, .Net nesneleri üzerinde yapılabilmesini sağlayan bir yapıdır. Select, Sum, Count, Where, Max, Min, Average gibi ifadeler metotlaştırılarak nesneler üzerinde kullanılabilir hale getirilmiştir. Bu metotlar System.Core.dll assembly’si içerisinde System.Linq namespace’i altındaki Enumerable adlı static bir sınf içerisinde bulunur. Static sınıflar içerisinde yalnızca static üyeler bulunabilir. Static metotlar da bir nesne üzerinden değil, içerisinde bulunduğu tip üzerinden çağırılabilir. Ancak C# 3.0 ile gelen yeniliklerden birisi de Extension(Genişletilmiş) metotlardı. Bunun detaylarından daha önce bahsetmiştim. “this” anahtar sözcüğü ile, static metotları, sanki bir non-static metot gibi nesne örnekleri üzerinden çağırabilme imkanına sahibiz. Linq mimarisini oluşturan yapı aslında, Enumerable sınıfında bulunan bu extension metotlardır.

image

Sınıf içerisinde yer alan metotları incelersek, Func<> tipinden parametreler ile karşılaşacağız. Func<>, generic bir delegedir. Bir metot parametre olarak bir delege alıyorusa, bizden bir metot bekliyor anlamına gelir. Çalışmasını istediğimiz metotu Func tipinden bir delege ile işaret ederek bu static metotlara parametre olarak vermek yerine, metotları anonim olarak yazmayı tercih edebiliriz. Anonim metot özelliği C# 2.0 ile gelen bir özelliktir. C# 3.0 ile gelen yeniliklerden biri de, anonim metot yazarken bize kolaylık sağlayan lambda operatörüdür. Buraya kadar olan kısımda konuşmuş olduğumuz yenilikleri, daha önceki makalelerimde ele almaya çalışmıştım. Şimdi de 3.0 ile gelen yeniliklerden olan ve LINQ mimarisinde sıkça karşılacağımız Anonim tipler ve “var” anahtar sözcüğünden bahsedeceğiz.

Anonim tip yazabilme kolaylığı ile birlikte, tasarımı yapılmamış sınıfların nesnelerinin oluşturulabiliriz. var anahar sözcüğü de, sadece local değişkenlere uygulanabilen, ilk değer ataması yapılması zorunlu olan (ilk değer olarak null atanamaz) ve geriye dönüş tipini bilmediğimiz, ya da yazması uzun olan ifadelerde kullanabildiğimiz bir keyword’dür. Bir örnek üzerinden gidersek anlaşılmasının daha kolay olacağını düşünüyorum.

class Program

    {

        static void Main(string[] args)

        {

            var nesne = new { Id = 1, Ad = "Masa", Fiyat = 300 };

        }

    }

Yukarıdaki tek satırlık kod, ilk bakışta anlamsız gelebilir. Ancak biraz incelendiği takdirde, Id, Ad ve Fiyat property’lerine sahip bir tip için nesne örneği oluşturduğumuzu görebilmek mümkündür. Bunu da local bir değişkende tutmak için var anahtar sözcüğünü kullandık. Çünkü geriye dönecek tip, .net içerisinde varolan ya da bizim tasarlamış olduğumuz bir tip değildir. Anonim olarak o anda oluşturulmuş bir tiptir. exe’nin IL codlarını incelediğimizde arka tarafta bir tip oluşturulduğunu ve property’lerinin tanımlandığını görmemiz mümkündür.

image

Gördüğünüz gibi, oluşan sınıfın 3 parametre alan constructor metodu, private field’ları ve property’leri tanımlanmıştır. Ayrıca Equals, GetHashCode, ToString gibi üyelere sahip olması da Object tipinden kalıtıldığını kanıtlar.

Aynı property’lere sahip anonim tipte bir nesne örnekleyelim.

class Program

    {

        static void Main(string[] args)

        {

            var nesne = new { Id = 1, Ad = "Masa", Fiyat = 300 };

            var nesne2 = new { Id = 2, Ad = "Sandalye", Fiyat = 80 };

        }

    }

Programı çalıştırıp oluşan exe’nin IL kodlarını bir kere daha incelediğimiz takdirde, yeni bir tipin oluşturulmadığını görürüz. İlk satırda yazmış olduğumuz tip ile birebir aynı olduğundan dolayı compiler tarafından algılanır.

Yeni bir nesne örneği oluşturalım, bu sefer sadece Id ve Ad property’leri olsun.

class Program

    {

        static void Main(string[] args)

        {

            var nesne = new { Id = 1, Ad = "Masa", Fiyat = 300 };

            var nesne2 = new { Id = 2, Ad = "Sandalye" };

        }

    }

IL kodları incelediğimizde aşağıdaki gibi bir tablo ile karşılaşacağız.

image

Görüldüğü gibi, Id ve Ad özellikleri olan yeni bir tip oluşturulmuştur.

C# 3.0 ile gelen bazı yeniliklerden kısaca bahsetmiş olduk, artık yavaş yavaş LINQ mimarisine geçebiliriz. Kısa bir tanım yapacak olursak LINQ , Enumerable sınıfında yer alan metotlardan oluşmuştur diyebiliriz. Bu extension metotlar, IEnumerable<T> generic interface’ini implement eden tiplerin nesne örnekleri üzerinden kullanılabilir. Dolayısı ile .Net içerisinde yer alan dizi, koleksiyon vb veri kaynakları üzerinde bu metotları kullanabiliriz. Şimdi birkaç örnek vermeye çalışalım.

Elimizde integer tipinden bir dizi olsun. Bu dizi içerisindeki çift sayıları elde edip ekrana yazdırmak istediğimizi varsayalım. Burada bir filtreleme işlemi söz konusu olduğundan dolayı Enumerable sınıfında yer alan Where metodunu kullanabiliriz.

image

Metot parametre olarak Func<int,bool> tipinden bir delege beklemektedir. Func<int,bool> delegesi üzerinden go to definition yaptığımızda, parametre olarak T alan ve geriye TResult dönen bir delege olduğunu görebiliriz. Bizim kullanacağımız versiyonda T tipi integer, TResult tipi de bool’dur. Şimdi bu delegenin işaret edebileceği bir metot yazalım.

static bool CiftMi(int sayi)

        {

            return sayi % 2 == 0;

        }

Şimdi de Func<int, bool> tipinden bir delege oluşturarak CiftMi adlı metodun adresini atayalım. Daha sonra delegeyi Where metodu içerisinde parametre olarak kullanalım. Where metodu geriye IEnumerable<T> tipinden bir koleksiyon döner. Yani, iterasyon yapıldığında çalışacak bir sorgu nesnesi döndürür. Bu tip üzerinde iterasyon yaparak çift sayıları ekrana yazdıralım.

class Program

    {

        static void Main(string[] args)

        {

            int[] sayilar = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

           

            Func<int, bool> delege = CiftMi;

           

            var sorguNesnesi = sayilar.Where(delege);

           

            foreach (int ciftSayi in sorguNesnesi)

            {

                Console.WriteLine(ciftSayi);

            }

        }

        static bool CiftMi(int sayi)

        {

            return sayi % 2 == 0;

        }

    }

image

Burada kullanmış olduğumuz “CiftMi” adlı metodunu uzun olarak tanımlamak yerine, lambda operatörü yardımı ile anonim metot yazabiliriz.

class Program

    {

        static void Main(string[] args)

        {

            int[] sayilar = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

 

            var sorguNesnesi = sayilar.Where(sayi => sayi % 2 == 0);

 

            foreach (int ciftSayi in sorguNesnesi)

            {

                Console.WriteLine(ciftSayi);

            }

        }

    }

Yukarıdaki iki kod örneğinin ekran çıktısı aynı olacaktır. Tek fark, ekstra bir metot tanımlamak yerine, delege yerine anonim bir metot yazarak sonuca ulaşmış olduk.

Lambda operatörü başlangıçta çoğu insana daha karmaşık gelmektedir. SQL syntax’ına alışkın olanlar için anlaşılması daha kolay olan başka bir ifade biçimini daha ele alalım.

class Program

    {

        static void Main(string[] args)

        {

            int[] sayilar = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

 

            var sorguNesnesi = from sayi in sayilar

                               where sayi % 2 == 0

                               select sayi;

 

            foreach (int ciftSayi in sorguNesnesi)

            {

                Console.WriteLine(ciftSayi);

            }

        }

    }

Yukarıdaki kod bloğu çalıştığında yine çift sayıları elde edeceğiz.

image

Bir sonraki makalede sıkça kullanılan LINQ metotlarını daha ayrıntılı bir şekilde ele almaya çalışacağım. Faydalı olması dileği ile…



0 yorum:

Yorum Gönder