C# 3.0 Yenilikleri

C# 3.0 ile birlikte programlama dili tarafına bazı yenilikler eklendi. Bu yenilikleri incelendiğinde bunların daha çok LINQ teknolojisiyle ilişkili olduğunu ve onun sorgularını desteklemek amacıyla sunulduğu görülür. Bu yazıda .NET 3.0 ile birlikte gelmiş yenilikler incelenmektedir.
1) Bilinçsizce Türlendirilmiş Değişkenler (Implicitly Typed Variables)
.NET 3.0 ile birlikte değişkenlerin türlerini belirtmeden tanımlama imkanı sağlandı.
var anahtar sözcüğünü kullanarak türünü vermeden bir değişken tanımlayabiliriz. Derleyici, değişkenin türünün ne olacağına değişkene verilmiş ilk değerden anlar. Bu yüzden bu tür değişkenlerin mutlaka ilk değeri verilmelidir.

var v1 = "Microsoft";
var v2 = 2007;
var v3 = false;
var v4 = new DataSet();
var v5 = new int[] { 1, 2, 3 };

Console.WriteLine(v1.GetType());
Console.WriteLine(v2.GetType());
Console.WriteLine(v3.GetType());
Console.WriteLine(v4.GetType());
Console.WriteLine(v5.GetType());

System.String
System.Int32
System.Boolean
System.Data.DataSet
System.Int32[]

Bu değişkenlerin türleri çalışma zamanında değil derleme zamanında belirlendiği için herhangi bir performans sorunu da yaşanılmamaktadır. Nitekim bu basit örneği ILDASM aracıyla incelediğimizde aşağıdaki MSIL kodunu görürüz.
.locals init ([0] string v1,
[1] int32 v2,
[2] bool v3,
[3] class [System.Data]System.Data.DataSet v4,
[4] int32[] v5)

Bu tür değişkenlerin ilk değerinin null değerinden farklı olması gerekir. Aşağıdaki değişken tanımları geçersiz sayılır.

var x;		   // HATALI, ilk değeri yok
var y = { 1, 2, 3 }; // HATALI, koleksiyon nesnesi oluşturulmamış
var z = null;	   // HATALI, ilk değer olarak null değeri girilmiş

2) Anonim/İsimsiz Veri Türleri (Anonymous Types)
.NET 2.0’daki isimsiz yordamlar gibi .NET 3.0 ile birlikte isimsiz değişken türü de tanımlama imkanı elde edilmiş oldu. Bu tür türlerden bir değişken tanımlamak için aynı şekilde “var” sözcüğü kullanılır. Bunun nasıl olacağını bir örnek üzerinde gösterelim.

var Kisi1 = new { KisiId = 1, AdSoyad = "Ali Korkmaz" };
var Kisi2 = new { KisiId = 2, AdSoyad = "Ayşe Korkmaz" };

//Aynı türden bu iki değişkeni birbirine eşitleyelim.
Kisi1 = Kisi2;

Console.WriteLine(Kisi1.KisiId +" » "+ Kisi1.AdSoyad);

2 » Ayşe Korkmaz
Kisi1 ve Kisi2 satırlarında görüldüğü gibi değişken ve değişkenin türü aynı anda oluşturulmuş oldu. Çalışma zamanında bu değişken türlerinin isimsiz türler olduğunu algılanır ve ona göre işlem yapılır.

Bu tür değişken türleri için derleyici, türün yapısına uygun bir veri türü oluşturur. Programın MSIL kodunu incelediğimizde integer türünde KisiId ve string türünde AdSoyad özelliklerine sahip bir sınıfın oluşturulduğunu ve Kisi1 ile Kisi2 değişkenlerinin bu sınıfın türünden tanımlandığını görürüz.

3) Nesne ve Koleksiyon İlklendirme (Object and Collection Initializers)
Bilindiği gibi bir sınıftan nesne yaratırken o sınıfın yapıcı yordamı kullanılır. Farklı sayıda veya türde parametre kullanılması için yapıcıların aşırı yüklenmesi sağlanır. .NET 3.0 ile birlikte nesnenin özelliklerini tek bir yapıcı üzerinden tanımlayabileceğiz. Böylece girilecek özelliklerin sırası veya sayısına göre birden fazla yapıcı yordam oluşturulmamış olacak.

public class Kisi {
    public int KisiId;
    public string AdSoyad;
    public AdresBilgi Adres;
}

public class AdresBilgi {
    public string CaddeSokak;
    public string Sehir;
}

Kisi Kisi1 = new Kisi {KisiId=1};
Console.WriteLine(Kisi1.KisiId + " » " + Kisi1.AdSoyad);

var Kisi2 = new Kisi { KisiId = 1, AdSoyad="Ali Korkmaz"};
Console.WriteLine(Kisi2.KisiId + " » " + Kisi2.AdSoyad);

AdresBilgi Kisi3Adres = new AdresBilgi {
    CaddeSokak = "X Cd. Y Sk.",
    Sehir = "İzmir" };
Kisi Kisi3 = new Kisi {
    KisiId = 1,
    AdSoyad = "Ali Korkmaz",
    Adres = Kisi3Adres
};

Console.WriteLine(Kisi3.KisiId + " » "+ Kisi3.AdSoyad
    +" » "+ Kisi3.Adres.CaddeSokak +" » "+ Kisi3.Adres.Sehir);

1 »
1 » Ali Korkmaz
1 » Ali Korkmaz » X Cd. Y Sk. » İzmir

Görüldüğü gibi Kisi isimli sınıftan aynı yapıcı yordam üzerinden parametre yapılarını farklılaştırarak üç farklı nesne oluşturmuş olduk.
Aynı mantıkla jenerik koleksiyon türündeki nesnelere başlangıç değerleri de bu şekilde verilebilir.

List
<strong>4) Genişletme Yordamları (Extension Methods)</strong><br />
.NET 3.0 ile birlikte var olan bir veri türünü yeni yordamlarla genişletme olanağı elde edildi. Bu veri türü, .NET Framework'ün hazır gelen derlenmiş bir sınıfı olabileceği gibi kendi yazdığımız bir sınıf ta olabilir. Genişletme yordamı yazmanın ilk kuralı, bu yordamların mutlaka statik bir sınıf içerisinde tanımlanmasıdır. İkinci kural ise genişletme yordamının ilişkili olduğu veri türü, yordamın ilk parametresinde <strong>this </strong>anahtar sözcüğüyle belirtilmesidir. Dolayısıyla genişletme yordamları mutlaka bir parametre almak zorundadır.<br />
.NET Framework'ün <em>System.Int32 </em>veri türü için bir genişletme yordamı yazalım. Bu yordam, çağrıldığı değerin karesini döndürecek.
[csharp]public static class GenisletmeYordamı {
    public static int KareAl(this int sayi) {
        return sayi * sayi;
    }
}

public class Program {
    public static void Main(string[] args) {
        int x = 9;
        int kare = x.KareAl();
        Console.WriteLine("9'ün karesi : {0}", kare);

        Console.ReadLine();
    }
}

9'ün karesi : 81
Bu yöntemin kullanışlı tarafı, var olan veri türünün yapısını bozmadan ona yeni yordamlar kazandırıyor olmamızdır. Visual Studio IntelliSense özelliği bu yordamlar için de geçerlidir. Aşağıdaki şekilde görüldüğü gibi KareAl() yordamı tamsayı türünün dahili bir yordamı olarak görülür.

Kendi yazdığımız bir veri türü için bir genişletme yordamı yazalım. Aşağıdaki örnekte KisiTur isimli yeni bir veri türü tanımlanmış ardından bu türü için Yazdir() isminde bir genişletme yordamı yazılmıştır.

public class KisiTur {
    public Int32 KisiId;
    public string AdSoyad;
}

public static class GenisletmeYordamı {
    public static void Yazdir(this KisiTur Kisi) {
        Console.WriteLine("{0} nolu müşterinin adı : {1}",
                    Kisi.KisiId, Kisi.AdSoyad);
    }
}

public class Program {
    public static void Main(string[] args) {
        KisiTur oKisi = new KisiTur();
        oKisi.KisiId = 1;
        oKisi.AdSoyad = "Ali Korkmaz";
        //KisiTur için yazılmış olan extend yordamı çağıralım
        oKisi.Yazdir();

        Console.ReadLine();
    }
}

1 nolu müşterinin adı : Ali Korkmaz
Genişletme yordamları birden fazla parametre de alabilir. Aynı şekilde ilk parametrenin türünün yordamın ilişkili olduğu veri türüyle aynı olmasına dikkat edilmelidir. Diğer parametreler bilinen normal yöntemle tanımlanır.

public static class GenisletmeYordamı {
    public static int Toplama(this int sayi1,int sayi2)  {
        return sayi1 + sayi2;
    }
}

public class Program  {
    public static void Main(string[] args) {
        int sayi = 10;
        int sonuc = sayi.Toplama(15);
        Console.WriteLine("İşlemin sonucu : {0}", sonuc);
    }
}

İşlemin sonucu : 25
NOT : Genişletme yordamlarının override özelliği bulunmamaktadır. Yani bir sınıfın içerisinde tanımlı olan bir yordamla aynı isimde bir genişletme yordamı tanımlandığı zaman asıl yordam ezilmez. Derleyici genişletme yordamını değil veri türüne ait asıl yordamı dikkate alır. Aynı şekilde veri türündeki bir özellik ile aynı isimde bir genişletme yordamı yazılırsa tasarım anında genişletme yordamına erişilemez ayrıca derleyici de genişletme yordamını görmezden gelir.
Genişletme yordamları diğer yordamlar gibi aşırı yüklenebilir. Yani bir genişletme yordamının aynı isimde ama parametre yapısı farklı olan versiyonları yazılabilir.

public static class GenisletmeYordamı {
    public static int Toplama(this int sayi1, int sayi2)     {
        Console.WriteLine("İkinci parametre Integer türünde");
        return sayi1 + sayi2;
    }

    public static double Toplama(this int sayi1, double sayi2) {
        Console.WriteLine("İkinci parametre Double türünde");
        return sayi1 + sayi2;
    }
}

public class Program {
    public static void Main(string[] args)     {
        int sayi = 10;
        Console.WriteLine("İşlemin sonucu : {0}", sayi.Toplama(15));
        Console.WriteLine("İşlemin sonucu : {0}", sayi.Toplama(15.4));
    }
}

İkinci parametre Integer türünde
İşlemin sonucu : 25
İkinci parametre Double türünde
İşlemin sonucu : 25,4

Genişletme yordamları, sınıfların yanısıra arabirim (interface) türündeki yapılar için de yazılabilir. Böylece arabirimden türemiş sınıflar da otomatik olarak söz konusu genişletme yordamını kullanmış olur.
Bu şekilde kaynak koduna erişebildiğimiz veya erişemediğimiz herhangi bir veri türüne, türünün yapısını bozmadan dışarıdan yeni yordamlar ekleyebiliyoruz. Eğer object türü için bir yordam yazılırsa doğal olarak tüm veri türlerinden bu yordama erişilmiş olur.
5) Lambda İfadeleri (Lambda Expressions)
Lambda ifadeleri, uygulama içerisinde karmaşık algoritmaları, uzun satırları daha kısa deyimlerle ifade etmemizi sağlar. .NET 3.0 ile birlikte Framework tabanlı dillerde kullanılmaya başlanan Lambda ifadeleri, Pyhton, Lisp gibi programlama dillerinde uygulama geliştirmiş kişilerin yabancı olmadığı bir konudur.
.NET platformunda Lambda ifadelerinin çıkış amacı genişletme yordamlarıyla birlikte LINQ sorgularını yazmak olsa da uygulama içerisinde farklı alanlarda kullanılması programcıya büyük kolaylık sağlamaktadır. Lambda ifadelerinin gereksinimini anlamak için delege/temsilci (delegate) yapısından yol çıkarak bu noktaya nasıl gelindiğine bakacağız. Delege yapısını ele almamızın nedeni LINQ deyimlerinin temsilci nesnelerinin parametre olarak çağrıldığı alanlarda kullanılıyor olmasıdır.
Bilindiği gibi .NET teknolojisinin ilk sürümünden beri temsilci yapılarına destek verilmektedir. Nasıl ki değişkenlerin türleri varsa yordamların da yapılarına uygun türleri bulunur. Bu türleri temsil eden yapılara temsilci denilir. Teknik bir tanım yapacak olursak temsilci’ler, yordamların hafıza üzerindeki konumuna işaret eder. Uygulama içerisinde herhangi bir yordamın konumuna ihtiyacımız olduğunda onu temsil eden temsilciyi çağırmamız yeterli olacaktır.
Aşağıdaki örneğimizde integer türünde iç ve dış parametreler sahip MyDelegate isminde temsilcinin kullanımı gösterilmiştir.

public class Program {
    public delegate int MyDelegate(int Deger); //Tanımlama

    public static void Main(string[] args) {
        MyDelegate oDlg = new MyDelegate(KareAl); //Örnekleme
        //Veya
        //MyDelegate oDlg = KareAl;

        int Kare = oDlg(5); //Çağırma
        Console.WriteLine("5'in karesi :{0}", Kare);
    }

    static int KareAl(int Sayi) {
        return Sayi * Sayi;
    }

}

5'in karesi :25
Bu örnekte ismi belirli olan bir yordam kullanıldı. Aynı örneği aşağıdaki gibi anonim yordam (anonymous method) yapısını tercih ederek daha az kod satırı yazabiliriz.

//  Temsilcinin temsil edeceği yordam isimsiz olarak tanımlanmıştır.
MyDelegate oDlg = delegate(int Sayi) { return Sayi * Sayi; };
int Kare = oDlg(5);

C# 3.0 ile birlikte bu tür işlemlerin sözdizimini kısaltmak için LINQ ifadeleri geliştirildi. LINQ ifadeleri “=>” operatörüyle yazılır. Örnekteki temsilciyi LINQ aracılığıyla aşağıdaki şekillerde ifade edebiliriz.

MyDelegate oDlg = (int sayi) => { return sayi * sayi; };

//return ifadesini kaldıralım
MyDelegate oDlg = (int sayi) => sayi * sayi;  

//Türü dönüşümünde sorun çıkmayacaksa aşağıdaki gibi de yazabiliriz
MyDelegate oDlg = sayi => sayi * sayi;

Aynı şekilde birden fazla parametre de yazılabilir.

//İki integer türünde parametre alan bir temsilci
public delegate int DlgToplam(int Sayi1, int Sayi2);

DlgToplam oDlg = (x,y) =>x+y;
int Sonuc = oDlg(7, 4);

Lambda ifadelerini, LINQ sorgularını yazarken kısa sözdizimiyle sorgunun okunurluğunu artırmaktadır.
Bu ifadelerin kolaylık sağladığı diğer alan da koleksiyon nesneleridir. Koleksiyon nesnelerinin temsilci türünde parametre alan yordamlarında Lambda ifadelerini kullanabiliriz. Örneğin koleksiyon tabanlı kaynaklarda arama yapmak için kullandığımız Find(), FindAll(), FindIndex() yordamları bu türden yordamlardır. Aşağıdaki örnekte koleksiyonda bulunan 5 haneli kayıtların listesi alınmaktadır.

List
Eğer lambda ifadelerini kullanmamış olsaydık bu basit örnek için aşağıdaki gibi harici bir yordam yazacaktık.
[csharp]var besliler = kisiler.FindAll(BesliBul);

static bool BesliBul(string ad) {
    return ad.Length == 5;
}

Lambda ifadelerini kullandığımız durumlarda derleyici ifadeye uygun yordam oluşturur. En son yaptığımızın örneği Reflector aracılığıyla kodunu incelediğimizde temsili bir yordamın oluşturulduğunu görürüz.

6) Parçalı Yordam (Partial Method)
NET 3.0 dasd asd ‘ın sunduğu yeni özelliklerinden diğeri de parçalı yordam (partial method) yapılarıdır. Bu yapı aracılığıyla bir yordamın tanımını ve içeriğini farklı dosyalarda saklayabileceğiz. Bu yordamlar parçalı sınıflar gibi “partial” sözcüğüyle tanımlanır.
Parçalı yordamlarla ilgili birkaç zorunlu madde bulunmaktadır:
Parçalı yordamlar ancak parçalı sınıflar içerisinde tanımlanabilir ve içeriği oluşturulabilir.
Parçalı yordamlar sadece parametre döndüremeyen yordamlar olabilir (void türü yordamlar)
Parçalı yordamlar yalnızca private nitelikli olabilir.

[Class1.cs]
partial class Musteri {
    string adsoyad;

    public string AdSoyad {
        get { return adsoyad; }

        set {asd
            adsoyad = value;
            MusteriKaydet(value);
        }
    }

    partial void MusteriKaydet(string name);
}

[Class2.cs]
using System;

partial class Musteri  {
    partial void MusteriKaydet(string name){
        Console.WriteLine("Müşteri bilgisi, veritabanına kayıt edildi.");
    }
}

Ana program içerisinde Musteri sınıfından bir örnek oluşturup parçalı yordamın yansımasını görelim.

public static void Main(string[] args) {
    Musteri oMs = new Musteri();
    oMs.AdSoyad = "Ahmet Kaymaz";
}

Müşteri bilgisi, veritabanına kayıt edildi.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir