Burak Selim Senyurt(MVP)
Matematik Mühendisi bir .NET Severin Yazıları...

Microsoft Teknoloji Günleri Akşam Sınıfı Başladı

Salı, 25 Mayıs 2010 23:00 by bsenyurt

Merhaba Arkadaşlar,

Bildiğiniz üzere bir süre önce Microsoft Türkiye ile birlikte Teknoloji Günleri Akşam Sınıfınını duyurmuştuk. Workshop tadında olan eğtimlerde ki amacımız, .Net Framework 4.0 tarafında gelen yenilikleri basit bir şekilde aktarmak ve tanıtmak. Bu gün başlayan servüvenimizin ilk dersinde, C# 4.0 ile Birlikte Gelen Yeniliklere değindik. 4 basit örnekle tanıdığımız yeni özelliklerde ilk olarak Reflection yerine dynamic kullanımını ve IronPython ile yazılmış bir kod içeriğinin çağırılmasını inceledik. Bu sayede dynamic diller ile olan etkileşimi de irdelemiş olduk. Ardından COM Interop tarafında gelen yenilikleri ve Optional, Named Parameters ve Ommiting Ref gibi konuları Office etkileşimi olan bir örnek üzerinden değerlendirdik. Son olarak Generic Covariance ve Contravariance konusuna giriş yaparak basit bir örnekle konuyu pekiştirmeye çalıştık.

Hafta içi gerçekleştirdiğimiz bu etkinliğe kayıt yaptırıp, özellikle iş yoğunluğu arasında zaman ayıran tüm katılımcılara canı gönülden teşekkür etmek istiyorum. Bir sonraki eğitimimiz 22 Haziran 2010 Salı günü yapılacak. Bu eğitimde ise .Net 4.0 ile Paralel Programlama konusuna giriş yapıyor olacağız. Eğlenceli ve ödüllü bir eğitim olacağını şimdiden belirtmek isterim. 

Bu günkü etkinliğimize ait sunum dosyası ve örneklere aşağıdaki linklerden ulaşabilirsiniz.  

C# 4.0 - New Features.pptx (474,28 kb)

Microsoft Yaz Okulu Gun 1.rar (1,18 mb)

C# 4.0 - Metod Overloading ve Dynamic Tipler

Salı, 13 Nisan 2010 13:40 by bsenyurt

Merhaba Arkadaşlar,

Eminim hepimiz çocukluğumuzda en az bir kere olmak üzere yediklerimizi, elimize yüzümüze bulaştırmış ve kirlenmişizdir. Her ne kadar bazı şirketler reklam kapmanyalarında kirlenmek güzeldir diyerek annelerin yüreğine su serpseler de, bu aslında pek gerçeği yansıtmamaktadır. Nitekim anneler, çocuklarının ellerini yüzlerini yediklerine bulayıp etraftaki eşyalara dokunmalarını pek hoş karşılamayabilirler. Ben şahsen bunu çocukken bir kaç kez tecrübe etmiş bir insanımdır. Yine de, yaşım hayatın yarısına merdiven dayamış olsa da, bazı zamanlarda o çok sevdiğim kayısı marmelatlı ve üstü pudralı olan Berliner tatlısını elime yüzüme(hatta burnuma) bulaştırarak yemeyi severim(Tabi evde ve en fazla eşimin yanında) Şimdi diyeceksiniz ki Burak Hoca gene başladı bir yiyecek ile... Embarassed Yok. Aslında odaklanacağımız nokta herşeyi ele yüze bulaştırmak. Şimdi öyle bir konuya dalacağız ki herşeyi karıştırıp allak bullak edip yüzümüze gözümüze bulaştıracağız. Öyleyse gelin hiç vakit kaybetmeden üstümüzü biraz kirletelim Wink

Bu gün kü yazımızda Dynamic tiplerin, metodların aşırı yüklenmesi(overload) durumunda nasıl bir duruma neden olduklarını incelemeye çalışıyor olacağız. Bildiğiniz üzere bir metodun aynı isme ait birden fazla versiyonu yazılabilmektedir. Bu durum kısaca metodların aşırı yüklenmesi(Overloading) olarak adlandırılmaktadır. Metodların aşırı yüklenmesindeki en büyük gayelerden birisi de, aynı amaca hizmet eden ama bunu farklı sayıda veya tipte parametre ile yerine getirebilen fonksiyonların farklı isimler ile yazılmasını engellemek ve böylece anlamsal bütünlüğü korumaktır. .Net Framework, ilk versiyonundan itibaren bu özelliği içermektedir. Çok eskiden eğitmenlik yaptığım dönemlerde, metodların aşırı yüklenmesi konusu ile ilişkili olarak verdiğim ilk örnek her zaman için Console sınıfının static WriteLine metodu olmuştur.

Şekilden de görüleceği üzere, WriteLine metodunun farklı tipte veya sayıda parametre ile çalışabilen 19 farklı versiyonu bulunmaktadır. Burada derleme zamanı açısından önem arz eden konulardan birisi de, metodların hangisinin çağırıldığının ayırt edilmesidir. İşte bu noktada metodun imzası(Signature) adı verilen kavram devreye girmektedir. Metod imzası, parametre sayısı veya tiplerini kapsamaktadır. Buna göre aynı tipten ama farklı sayıda parametre veya farklı tipten ama aynı sayıda parametre, çoğunlukla geçerlidir. Upsss...Çoğunlukla mı? Surprised Neden böyle söylediğimi ispat etmek için C# 4 ile birlikte gelen dynamic tiplerin, metodların aşırı yüklenmesi sırasındaki kullanımlarına göz atmamız yeterlidir. Öncelikli olarak aşağıdaki kod parçasını göz önüne alalım.

namespace DynamicAndOverloading
{
    class Program
    {
        static void Topla(int x) { }
        static void Topla(int x, dynamic y) { }
        static void Topla(dynamic x, int y) { }   

        static void Main(string[] args)
        {
            Topla(4);
            Topla(2,3);
        }
    }
}

Topla metodunun 3 farklı versiyonunun yazıldığı görülmektedir. Teorimize göre, tüm metodlar birbirlerinde farklıdır. Nitekim metod imzası kriterleri sağlanmaktadır. İlk Topla metodu tek parametre aldığı için iki parametre alan diğer versiyonları ile otomatik olarak ayrışmaktadır. Diğer yandan iki parametre alan versiyonlarda da, parametrelerin tipleri farklıdır. Farklıdır, nitekim sıraları aynı değildir. Dolayısıyla herhangibir sorun görünmemektedir. Oysaki daha Toplam metodunun iki parametreli versiyonunu yazarken, aşağıdaki ekran görüntüsünde yer alan hata mesajı ile karşılaşılır.

Mesaja göre derleyici, Topla(int,dynamic) ile Topla(dynamic,int) çağrıları arasında kararsız kalmıştır. Açıkçası ortada tam bir belirsizlik söz konusudur. Peki bu durum size tanıdık geldi mi? Aslında dynamic tip yerine object tipini kullandığımızda da benzer bir sorunla karşılaşırız. Aynı örnek kod parçasında bu sefer dynamic yerine object tipini kullandığımızı düşünelim.

Durum değişmemiştir. Derleyici yine hangi metodu çağıracağı konusunda kararsız kalmış ve hata mesajı üretilmesine sebebiyet vermiştir. Dolayısıyla program çalışmamaktadır.

Bu noktada metodların aşırı yüklenmesinde object tipi ile dynamic tiplerin benzer bir davranışa neden olduklarını düşünebiliriz. Ancak bunun ispatını da yaparsak ballı kaymaklı tap taze beyaz ekmek yemiş kadar oluruz. Laughing Şimdi durumu net bir şekilde ispatlamak adına kodu aşağıdaki gibi değiştirelim.

namespace DynamicAndOverloading
{
    class Program
    {
        static void Topla(int x) { }
        static void Topla(int x, dynamic y) { }
        //static void Topla(dynamic x, int y) { }  // Yorum satırı yaptık 

        static void Main(string[] args)
        {
            Topla(4);
            Topla(2,3);
        }
    }
}

Bu durumda program kodu başarılı bir şekilde derlenecektir. Şimdi belki de uzun zamandır yanına uğramadığımız Red Gates .Net Reflector aracını açalım ve program kodumuzun içeriğine bir bakalım. Aşağıdaki ekran görüntüsünde yer alan sonuçlar ile karşılaşırız.

Dikkatinizi çeken bir şey oldu mu? Wink 

Topla metodunun iki parametre alan versiyonunun IL(Intermediate Language) tarafına olan aktarımına göre dynamic tip olarak tanımladığımız y değişkeni, aslında Object tipi olarak değerlendirilmektedir. Bir dakika... Peki çalışma zamanı bunun aslında dynamic bir tip olduğunu nasıl anlayacaktır? Bunu Topla(Int32,Object): Void metodunun C# kodu çıktısına bakarak görebiliriz. İşte .Net Reflector çıktısı.

Görüldüğü üzere object y değişkeni [Dynamic] niteliği ile imzalanmıştır. Dolayısıyla çalışma zamanı tarafından aslında dynamic tip olarak yorumlanacaktır.

Buraya kadar her şey netleşmiş gibi düşünülebilir ve hatta yazımızın artık bitmemesi için bir neden olmadığı da düşünülebilir. Ancak yazımızı sonlandırmadan önce, object tipinin metodların aşırı yüklenmesinde yol açtığı sorunun, diğer tipler içinde geçerli olabileceğini gösterek ilerlememizde yarar vardır. Söz gelimi aşağıdaki şekilde yer alan kod parçasında da benzer durumun oluştuğu görülebilir.

Dikkat edilecek olursa derleyici, double tipinin kullanıldığı Topla metodlarından hangisinin kullanılacağı konusunda kararsız kalmaktadır. Yoksa metod imzası kavramı çatlamakta mı? Aslında bir çözüm söz konusudur. O da, doğru değerler ile ilgili metodların çağırılmasıdır. Yani gerçekten double tip ile çağrı yapılmasıdır. Bu durumda aşağıdaki kod parçası derleme zamanı hatasına yol açmayacaktır.

namespace DynamicAndOverloading
{
    class Program
    {
        static void Topla(int x) { }
        static void Topla(int x, double y) { }
        static void Topla(double x, int y) { }   

        static void Main(string[] args)
        {
            Topla(4);
            Topla(Math.PI,3);
        }
    }
}

Derleme hatası olmaması son derece doğaldır. Nitekim Math.PI değişkeninin kullanılması, Topla(double x,int y) metodunun tespit edilmesini sağlamaktadır. Şimdi olay biraz daha ilginç bir hal almaya başlayacaktır. Bunun için program kodunu aşağıdaki şekilde güncelleyip devam ettiğimizi düşünelim.

namespace DynamicAndOverloading
{
    class Program
    {
        static void Topla(int x) { }
        static void Topla(int x, dynamic y) { }
        static void Topla(dynamic x, int y) { }   

        static void Main(string[] args)
        {
            Topla(4);
            Topla(Math.PI,3);
        }
    }
}

Uygulamayı derlediğimizde her hangibir hata mesajı ile karşılaşmadığımızı görürüz.

Oysa ki az önce dynamic tipin kullanıldığı senaryoda kararsızlık yaşandığına gözümüzle şahit olmuştuk. Peki ya şimdi ne oldu da sorun çözüldü? Aslında sorun değerlerin farklılaştırılması ile çözüm bulmuştur. Dikkat edilecek olursa Topla(2,3) çağrısı derleyicinin kafasının karışmasına neden olurken, Topla(Math.PI,3) çağrısında bu sorun oluşmamıştır. Tahmin edeceğiniz üzere object tipi için yaşanan sorunda farklı tipteki değerlerin kullanılması halinde çözülecektir. Ve çok doğal double tipini kullandığımız vaka için de çözüm olacaktır. Wink 

Tabi bu noktada şunu da belirtmekte yarar vardır. Söz konusu metodların ayrı bir kütüphane içerisinde tanımlanması halinde, bu kütüphaneyi kullanan tiplerin yazıda ele aldığımız hatalara düşme olasılıkları bulunabilir ki bu da istenen bir durum değildir. Dolayısıyla C#' ın temel kavramlarından birisi olarak ele alınana metodların aşırı yüklenmesi(Overload) aslında derinlerine inildiğinde dikkatli olunmayı gerektirecek vakaları içermektedir. Aynen yazımızda ele aldığımız üzere. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

[Örnek kodlar Visual Studio 2010 Ultimate RTM sürümü üzerinde geliştirilmiş ve test edilmiştir]

Object vs Dynamic

Perşembe, 1 Nisan 2010 00:10 by bsenyurt

Merhaba Arkadaşlar,

Ayrıntılar detaylarda saklıdır. Bu cümleyi çok severim. Sevdiğim özlü sözler arasındadır. Gerçel bir nesnenin ne kadar kaliteli olduğunu anlamak için detaylarına bakmak gerekir. İşçiliğine, kullanılan malzemeye, malzemelerin uyumuna vs...Hatta benzer diğerleri ile olan kalite farkını anlamak için bile. Çok doğal olarak yazılım dünyasında da bir takım konuların anlaşılabilmesi, kavranabilmesi, benzerleri ile olan farklarının irdelenebilmesi için mutlaka detaylara bakmak, ama sıkılmadan bakmak gerekir. Aynen bu günkü yazımızda yapacağımız gibi.

Bu yazımızda Dynamic Language Runtime kullanımında büyük öneme sahip olan dynamic ile .Net Framework' ün ilk çıktığı zamandan beri var olan Object tipi arasındaki farklılıkları görmeye çalışacağız. Bunun için kod tarafında biraz daha detaya girmemiz gerekecek. Çok derin değil belki de ama aradaki farklılıkları çıkartabilmek adına önemli detaylar. Başlamadan önce örneklerimizi Visual Studio 2010 Ultimate RC sürümünde geliştirdiğimizi ve ilerleyen sürümlerde farklılıklar olabileceğini hatırlatmak isterim.

Öncelikli olarak işe aşağıdaki basit kod parçası ve Object tipini ele alarak başlayalım.

Case 1;

object pi = Math.PI;
Console.WriteLine(pi.GetType().ToString());

İlk satırda Math.PI sabit değerinin object tipine atandığını görüyoruz. Aslında bilinçsiz olarak bir tür dönüşümü söz konusu(Implicitly Type Casting). Burada herhangibir sıkıntı yok. pi.GetType satırının çalışma zamanı çıktısı ise System.Double olmalıdır. Nitekim eşitliğin sağ tarafından gelen Math.PI, double tipinden olduğu için pi isimli object tipi üzerinden ele alınsa da kendi tipini taşımış olmaktadır.

Şimdi kodumuzu aşağıdaki gibi değiştirdiğimizi düşünelim.

Case 2;

object pi = Math.PI;
object square = pi * 10 * 10;

Bu kez object tipinden olan pi ile iki sayısal değeri(ki bunlarda int tipindendir) matematiksel bir işleme tabi tutup sonucu yine bir object tipine atamaya çalışıyoruz. Ancak kodu derlediğimizde Compiler' ın bir derleme zamanı hata mesajı ürettiğini görürüz.

Hımmm...Aslında bu son derece doğal bir sonuç. Nitekim derleme zamanında pi değişkeninin tipi object ve biz object tipi ile int tiplerini işleme sokmaya çalışıyoruz. Burada çözüm, dönüştürme işlemini bilinçli olarak yapmaktan ibaret(Explicitly Type Casting). Yani kodu aşağıdaki gibi düzenlemekten;

Case 3;

object pi = Math.PI;
object square = (double)pi * 10 * 10;

Dikkat edileceği üzere pi değişkeni bilinçli olarak double tipine dönüştürülmüş durumdadır. Aslında az önceki senaryomuzda pi' nin taşıdığı tipin double olduğunu görmüştük. Yine de başka tipler ile işleme tabi tuttuğumuzda derleme zamanı hatası ile karşılaşmaktan kurtulamadık. Çünkü pi değişkeni double tipten değer taşıyan bir object idi aslında.

Şimdi durumu biraz daha entersan bir hale getireceğiz. Aşağıdaki kod parçasını göz önüne alın ve kodu kafanızda derleyip hata olup olmadığını söyleyin. Sonrasında ise çalışma zamanında bir hata oluşup oluşmayacağını düşünün ve bir karar daha verin. En sonunda ise bunu, konu ile ilgili yakın arkadaşlarınızla tartışın Wink

object pi = Math.PI;
object square = (int)pi * 10 * 10;

Derleme zamanında herhangibir hata mesajı alınmayacaktır. Ancak çalışma zamanına(Runtime) geçtiğimizde aşağıdaki ekran görüntüsünde yer alan istisna(Exception) ile karşılaşırız.

Upsss!!!

Sorun şudur; pi değişkeni object tipinden tanımlanmıştır ve eşitliğin sağ tarafına göre double tipinden bir değer taşımaktadır. Ancak bu tip cast operatörüne göre int tipine dönüştürülmeye çalışılmaktadır. Oysaki çalışma zamanının burada beklediği tam olarak double tipine dönüştürme işlemidir.

Dolayısıyla object tipini kullandığımız bu senaryoda matematiksel işlemlerin yapılabilmesi için, mutlaka doğru tipe dönüşüm gerçekleştirilmelidir. Eğer bir dönüştürme işlemi yapmassak, derleme zamanında hata mesajı alırız. Ancak tip dönüşümü yaparkende koltuğumuzda rahat edemeyiz, çünkü yanlış tipe dönüştürme işlemleri çalışma zamanı istisnaları ile cezalandırılır. Buna göre gerçekten beklenen tipe dönüşüm işlemi sağlanmalıdır.

Gelelim yeni gözdemiz olan Dynamic tipe. Yukarıdaki senaryoları bire bir, ama bu kez dynamic tipini kullanarak değerlendireceğiz.

dynamic pi = Math.PI;
Console.WriteLine(pi.GetType().ToString());

Bu kez eşitliğin sol tarafında dynamic tipi vardır. object tipinin kullanıldığı örnektekine(Case 1) benzer olaraktan pi değişkeni yine double tipinden bir değer taşımaktadır. Çalışma zamanının üreteceği sonuçta aynı olacaktır. Ancak aşağıdaki kod parçasını göz önüne aldığımızda,

dynamic pi = Math.PI;
dynamic square = pi * 10 * 10;

object tipinin kullandığımız örneğe(Case 2) baktığımızda derleme zamanında bir hata aldığımızı hatırlıyoruzdur sanırım. Oysaki dynamic tipi derleme zamanı ile ilgilenmemektedir. Çalışma zamanında ise pi' yi gereken tipte ele almaktadır. Zaten object tipini kullandığımız üçüncü senaryomuzu ele almamıza da gerek kalmamıştır. Wink

Buna göre şöyle bir sonuca varabiliriz. Dynamic tipin kullanıldığı hallerde derleyicinin(Compiler) derleme zamanında tip tahmini yapmasına gerek yoktur. Bu çözümleme işi çalışma zamanında yapılmaktadır.

Tabi bu sonuçlara göre "her yerde dynamic tip kullanalım mı?" sorusu da gündeme gelir. Ancak "metodlara parametre aktarımlarında dynamic kullanımının kodun kırılmasına neden olması söz konusu olabilir mi?" sorusu daha da önemlidir. Şimdi bu durumu ele almaya çalışalım. İşte kodlarımız;

            dynamic R = "on iki";
            Calculate(R);

            #endregion

            #endregion

        }

        static double Calculate(double r)
        {
            return Math.PI * r * r;
        }

Bu kod parçasında kodu kırmaya yönelik olarak bir hamle yapıldığı düşünülebilir. Calculate metodu double tipinden bir değer beklemektedir. Biz ise dynamic olarak tanımladığımız R değişkenine string bir değer atayarak parametre gönderme işlemini gerçekleştirmekteyiz. Dynamic kullanımına göre derleme zamanında bir hata mesajı alınmaması normaldir. Benzer şekilde çalışma zamanında gelen string tipinin, double tipe otomatik olarak dönüştürüleceğini de düşünebiliriz. Eğer böyle olsaydı, kodun dynamic tipi yardımıyla kolayca kırılabileceği sonuçlarına varabilirdik. Şükür ki çalışma zamanında aşağıdaki hata mesajı ile cezalandırılırız. Laughing

Ancak,

dynamic R=12;

atamasını yaparsak herhangibir sorun ile karşılaşmayız. Çünkü Calculate metodunun beklediği(veya taşıyabildiği) tipte bir değişkenin gönderilmesi sağlanmaktadır.

Sonuç olarak bir metoda dynamic tipte veri atayabiliyor olsakta bu, metoda her tipten değeri aktarabileceğimiz anlamına gelmemektedir. Gerçekten metodun beklediği veya kabul edebileceği türden bir değişkenin atanması şarttır.

Son senaryomuzu object tipi ile düşündüğümüzdeyse yine başta açıklanan 3 vakanın gerçekleşeceği görülecektir. Gelin bu durumları bir kere daha inceleyelim. Kodu ilk etapta aşağıdaki gibi düzenleyelim.

object R = 12.1;
Calculate(R);

Bu durumda daha derleme zamanında hata mesajı alırız. Aşağıdaki şekilde olduğu gibi.

Dolayısıyla tip dönüşümü yapmamız şarttır. Öyleyse yapalım. Laughing

object R = 12.1;
Calculate((int)R);

Derleme zamanında hata yok. Süper...Ama oda ne? Çalışma zamanında yine hata aldık. Undecided

Tahmin edileceği üzere object tipi kullanıldığından çalışma zamanında tam olarak double tipinden bir değer taşınması beklenmektedir. 12.1 double olarak ifade edilmesine rağmen int tipine yapılan dönüşüm geçersizdir(Dynamic tipin kullanımının tam aksine). Dolayısıyla kodun aşağıdaki gibi düzenlenmesi gerekir.

object R = 12.1;
Calculate((double)R);

Bu durumda çalışma zamanında her hangibir hata mesajı alınmadan ilerlenebilecektir.

Görüldüğü üzere derinlerde, dynamic ve object kullanımları arasında belirgin farklılıklar bulunmaktadır. Bunların sebepleri aşikardır. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek, hepinize mutlu günler dilerim.

DynamicVsObject_RC.rar (20,73 kb) [Örnek Visual Studio 2010 Ultimate RC sürümü üzerinde geliştirilmiş ve test edilmiştir]

Webiner - C# 4.0 - Yenilikler [Beta 2]

Perşembe, 7 Ocak 2010 02:00 by bsenyurt

Merhaba Arkadaşlar,

Çok eskiden bir Delphi programıcısydım. Açıkçası o dönemlerde Delphi geliştirme ortamının hayranı olduğumu itiraf etmeliyim. Delphi 1.0 ile başlayan profesyonel iş yaşantımda ilk geliştirmeye çalıştığım ve neredeyse para kazanmak üzere olduğum programı Üniversiteden çok değerli bir sınıf arkadaşım(Orkun Şentürk) ile birlikte yazamamıştık. Undecided Arkadaşımın motorsiklet satın aldığı bir mağazaya indirim karşılığında basit bit stok takip programı yazacaktık. Ne varki arkadaşım bir gün motorsikletinin sinyal lambasını kırdı ve 100 Mark değerinde olan lambayı karşılamak için programa yeni özellikler dahil ediliverdi.Laughing

O zamanlar Delphi 2.0 sürümüne henüz kavuşamamıştık ve Delphi 1.0 sürümünde veritabanı işlemleri ile ilişkili önemli sıkıntılar vardı. Sonuçta programı yazamadık. Onun yerine bir muhasebe programı satın almalarını(MSDOS tabanlı) önerdik. Tabi yine aynı arkadaşımla şeytanın bacağını bir sonraki projemizde kırdık ve yine Delphi 2.0 ile başarılı bir program yazıp satmayı başardık.

Ancak profesyonel yazılım geliştirme yaşantım zaman içerisinde Anders Heijslberg(C# ın babası diyebiliriz)' in Microsoft takımına geçmesiyle tamamen yön değiştirdi. Anders' in Microsoft'a geçişinden kısa bir süre sonra .Net Framework platformunu ve C# programlama dilini duyduk. Akabinde tabiki Visual Studio.Net geliştirme ortamı. C# programlama dilini daha görür görmez benimsemiş ve etkilenmiştim. Zaman ilerledikçe C# programlama dilide önemli gelişmeler gösterdi tabiki. Çünkü yazılımların ihtiyaçları, gereksinimleri günden güne artıyor ve programlama dillerini kendilerine uymaları konusunda zorluyordu.

Derken .Net Framework 2.0 köklü bir değişikliğin eklendiğini gördük. Generic mimari. Köklüydü çünkü CLR(Common Language Runtime) üzerinde değişikliklerin yapılmasını gerektiriyordu. Çok doğal olaraktan dil içerisinde buna destek verecek yenilikler yapıldığına ve dile özgü başka ilavelerin getirildiğine tanık olduk. Anonymous metodlar, yield anahtar kelimesi, static sınıflar vb...Tabiki yapılan yeniliklerin belirli amaçları olduğu kesindi. Bu amaçların iyice belirgineştiği yer ise .Net Framework 3.5 sürümüydü. Bu vesileyle Language INtegrated Query(LINQ) hedefli olarak gelen yenilikler ve bunun için dilde yapılan geliştirmelerle karşılaştık. Extension metodlar, Anonymous tipler, lambda(=>) operatörü, Auto Property' ler, Object Initializer' lar, partial event' ler vb...

Uzun bir süredirde C# 4.0 dili birlikte gelmesi muhtemel yenilikler söz konusu. Halen daha Beta 2 aşamasındayız ancak yakın zamanda RC versiyonu ile birlikte nihai sürüme ulaşacağımızı biliyoruz. (Her ne kadar zamanında bazı kişiler fake olarak C# 4.0 yeniliklerini bloglarında duyurup yayınlasalarda gerçekleri yansıtmıyorlardı) Özellikle Dinamik Dil Çalışma Zamanı(Dynmaic Language Runtime) ile olan etkileşimin arttırılmasını hedef alan yeni dil özelliklerini NedirTV?com adına düzenlenen webinerimizde detayları ile inceledik. Umarım sizler için yararlı olmuştur.

Süre 58:52

C# 4.0 - Invariance, Covariance, Contravariance ???

Salı, 22 Aralık 2009 16:15 by bsenyurt

Merhaba Arkadaşlar,

Bundan yıllar önce(aslında 2005 yılında...Çok eski bir tarih gibi görünmese de yazılım dünyası için çok çok uzun zaman önce anlamına gelmekte.) daha genç bir makale yazarıyken C# 2.0 delegate tiplerinde co-variance, contra-variance durumlarını incelemeye çalışmıştım. Kişisel görüşüme göre, anlaşılmasından ziyade iyi bir şekilde analiz edilerek anlatılması çok zor olan bir konu Co-Variance, Contra-Variance. Üstelik bu kavramların çıkış noktasında yer alan Variant, Invariant tip kavramları  düşünüldüğünde konuyu anlamak için epey bir çaba sarf etmemiz gerekebiliyor. Hatta anlayamadığımız durumlarda neredeyse bulunduğumuz duruma isyan eder bir hale gelebiliyoruz. Bu tip zor konularda benim öğrenmek üzerine uyguladığım strateji aslında pek çoğumuzun da uyguladığı bir yöntem. Önce sorunu örnekler ile anlamaya çalışmak, getirilen çözümü görmek ve en son olarak tanımlamaları yapmak. Bu önce kavram tanımlaması, sonra örnek uygulamanın yapılmasından ziyade daha etkili bir öğrenme şeklidir diye düşünüyorum. Öyleyse vakit kaybetmeden analizimize başlayalım.

.Net' in başından beri...

Aslında .Net' in ilk duyurulduğu ve C#, Vb.Net gibi nesne yönelimli yönetimli dillerin(Managed Languages) dünyaya geldiği anlardan bu yana kalıtımsal ilişkide olan tipler arasında bazı referans geçişlerinin yapıldığını bilmekteyiz. Burada tiplerin polimorfik özellikte olabilmelerinin de payı büyük. Bu sebepten .Net Framework' ün tüm sürümlerinde aşağıdaki gibi bir kod parçası olası.

using System;

namespace Before
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteString(35);
            WriteString("Burak Selim Şenyurt");
            WriteString(true);
            WriteString(new Album { AlbumID = 1, Title = "Chikenfoot" });
        }

        static void WriteString(object obj)
        {
            Console.WriteLine(obj.ToString());
        }       
    }
    class Album
    {
        public int AlbumID { get; set; }
        public string Title { get; set; }
    }   
}

Bu kod parçasında yer alan WriteString metodu tüm .Net tiplerinin atası olan object tipinden parametre almaktadır. Bu ata tip-alt tip ilişkisi nedeniyle metoda herhangibir .Net tipinin atanması söz konusudur. Üstelik herkes bir Object tipi olduğundan ToString metodunu uygulamakta veya uygulamasa bile object tipinin varsayılan ToString metodu çalıştırılabilmektedir. Nitekim Album tipi içerisinde ToString metodu ezilmemiş olmasına rağmen Object sınıfındaki varsayılan ToString metodunun çalıştırılması sağlanmıştır. (Bildiğiniz üzere ToString metodu object tipi içerisinde virtual olarak tanımlanmıştır ve alt tiplerde ezildiği takdirde objcet nesne referansı üzerinden ezilen versiyonunun çalıştırılması söz konusudur.)

Yolumuza devam edelim ve bu sefer aşağıdaki kod parçasını göz önüne alalım.

static void Main(string[] args)
{
   object albm = CreateAlbum(2, "Is There a Love in space[Joe Satriani]");
}
static Album CreateAlbum(int albumId,string title)
{
   return new Album{AlbumID=albumId,Title=title};
}

Bu kod parçasında yer alan CreateAlbum metodu Album tipinden bir değer döndürmektedir. Main metodu içerisinde ise CreateAlbum çağrısı sonucunun object tipine atanması söz konusudur. Her iki kod parçasınında çalışma zamanında veya derleme zamanında bir hata üretmesini beklemeyiz. Şimdi koltuklarınıza yaslanın ve takip eden paragrafı okuyun...

İlk kod parçasının çalışması doğaldır nitekim .Net' in tüm sürümlerinde parametreler Covariant tiptedir. Diğer yandan ikinci kod parçasının da çalışması doğaldır çünkü dönüş tipleride Contravariant' tır.

Bir şey anladınız mı? Açıkçası ben halen daha durumu tam olarak netleştiremediğimizi düşünüyorum. Öyleyse...

Generic mimariden önceki koleksiyonlarda durum...

İşin içerisine object tipinden değerler ile çalışan generic mimari öncesi koleksiyonlar girdiğinde, Covariance veya Contravariance olmanın artık güvenli olup olmadığından söz edilmeye başlanmaktadır. Güvenlik tip bazındadır. Şimdi bu durumu anlamaya çalışarak devam edelim.

ArrayList albumList = new ArrayList();
albumList.Add(new Album { AlbumID = 1, Title = "Chikenfoot" });
albumList.Add(new Album { AlbumID = 2, Title = "Is There a Love in space[Joe Satriani]" });
albumList.Add(new Album { AlbumID = 3, Title = "Big Blue Ball [Peter Gabriel]" });

ArrayList gibi koleksiyonların object tipi ile çalışmalarının sebebi herhangibir tip için koleksiyon bazlı özelliklerin kullanılabilmesini sağlamaktır (Tabi bu, tip güvensiz-unsafe bir durumu oluşturmuş ve sonrasında generic mimari getirilmiştir). Diğer yandan object ile çalışmaları nedeniyle, herhangibir .Net tipini bünyesinde barındırabilirler. Buna göre ilk örneğimizi ve bu son kod parçasını göz önüne alırsak koleksiyonların Covariance özelliğini sağladığını(yani Covariant tipte olduklarını) düşünebiliriz. Ancak buda tam olarak doğru değildir. Şimdi aşağıdaki kod parçasını göz önüne alalım.

using System;
using System.Collections;

namespace Before
{
    class Program
    {
        static void Main(string[] args)
        {
            ArrayList albumList = new ArrayList();
            albumList.Add(new Album { AlbumID = 1, Title = "Chikenfoot" });
            albumList.Add(new Album { AlbumID = 2, Title = "Is There a Love in space[Joe Satriani]" });
            albumList.Add(new Album { AlbumID = 3, Title = "Big Blue Ball [Peter Gabriel]" });
            albumList.Add("Reality Killed The Video Star [Robbie Willams]");

            WriteAlbumList(albumList);
        }

        static void WriteAlbumList(ArrayList albums)
        {
            foreach (Album albm in albums)
            {
                Console.WriteLine(albm.ToString());
            }
        }
    }
    class Album
    {
        public int AlbumID { get; set; }
        public string Title { get; set; }

        public override string ToString()
        {
            return String.Format("{0} {1}", AlbumID.ToString(), Title);
        }
    }   

Çok doğal olarak foreach döngüsü içerisinde albums koleksiyonu üzerinde dolaşılırken sadece Album tipleri ele alınmak istenmiştir. Ancak birisi kazayla albumList isimli koleksiyona string tipte bir değişken göndermiş ve yukarıdaki çalışma zamanı hatasının alınmasına neden olmuştur. Aman tanrımmmm!!! Sealed Şimdi koleksiyonlar için bahsedilen Covariant tipte oldukları gerçeği Un-Safe Covariant olarak düzeltilmelidir. Nitekim tip güvenliğinin garanti altına alınması mümkün olmamıştır. Hatta pek çok kaynak object tipi ile çalışan koleksiyonların aslında tamamen Invariant olduklarını ifade etmektedir. Kafamız gittikçe karışıyor değilmi. Öyleyse...

Peki ya diziler(Arrays)...

Aşağıdaki ekran görüntüsünde yer alan kod parçasını göz önüne alalım.

WriteAll metodu object tipinden bir dizi ile çalışmaktadır. Buna göre string tipinden bir dizinin bu metoda parametre olarak aktarılması mümkündür. Burada Covariance olma durumu söz konusudur. Elbette güvensiz olan versiyonu. Çünkü WriteAll metodu içerisinde bir tip güvenliği yoktur. Diğer yandan ilginç olan durum int tipinden bir diziyi göndermek istediğimizde ortaya çıkmaktadır. Böyle bir durumda derleme zamanı hatası alınacaktır. Int tipi değer tipi olduğundan object gibi bir referans türüne atanması mümkün değildir. İşte bu noktada object tipinden dizinin Invariance özellik gösterdiğini söyleyebiliriz.

Buna göre; Diziler bazı durumlarda Invariant bazı durumlarda ise Covariant tip olarak görülebilirler. Ancak Covariant olsalar dahi tip güvensiz olacaklardır(Unsafe). Diğer yandan değer türlü(Value Type) diziler her zaman için Invariantce özellik gösterir.

Generic koleksiyonlar...

Gelelim tip güvenli(Type Safe) olan generic koleksiyonlara. Generic mimari .Net içerisinde bir devrim yaratmış ve major versiyonlamaya gidilmesine neden olmuştur. Generic mimari sayesinde örneğin koleksiyonların sadece söylenen tiple çalışabileceği daha kodlama zamanındayken belirtilebilmekte ve böylece çalışma zamanında tip güvenliğinin aşılması engellenmektedir. Peki ya aşağıdaki kod parçasını göz önüne aldığımızda;

using System;
using System.Collections;
using System.Collections.Generic;

namespace Before
{
    class Program
    {
        static void Main(string[] args)
        {
            WriteAll(new List<Album>{
                new Album { AlbumID = 1, Title = "Chikenfoot" },
                new Album { AlbumID = 2, Title = "Is There a Love in space[Joe Satriani]" }
            }
            );

        }
        static void WriteAll(IEnumerable<object> parameters)
        {
            foreach (object parameter in parameters)
            {
                Console.WriteLine(parameter.ToString());
            }
        }
    }
    class Album
    {
        public int AlbumID { get; set; }
        public string Title { get; set; }

        public override string ToString()
        {
            return String.Format("{0} {1}", AlbumID.ToString(), Title);
        }
    }   
}

Burada WriteAll metodu IEnumerable<object> tipinden bir parametre almaktadır. Buna göre tüm T generic tiplerinin object tipinden türeyeceği düşünüldüğünde herhangibir sorun olmayacağı sonucuna varılabilir (En azından kafamızdaki compiler bu kodu sorunsuz olarak derleyecektir. İlk etapta...Wink) Yani Covariance olma durumu söz konusudur diyebiliriz...Mi acaba? İşte derleme zamanının bize vereceği cevap...

Görüldüğü üzere Album tipi ile çalışan koleksiyonun object tipine dönüştürülemeyeceğini belirten bir hata mesajı ile karşı karşıyayız. Buna göre generic koleksiyonların aslında Invariant tipte olduklarını ifade edebiliyoruz. Bu nedenle generic koleksiyonlar ne Covariance nede Contravariance özellik göstermektedir. Aslında generic koleksiyonlarda özel bir durum olarak T tipleri için türetmenin söz konusu olmadığını söyleyebiliriz. Bu sebepten IEnumerable<Album> ün IEnumberable<object> tarafından taşınması söz konusu olmamaktadır. Birde aşağıdaki kod parçasını göz önüne alalım;

using System;
using System.Collections;
using System.Collections.Generic;

namespace Before
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<object> albums = GetAlbums();
        }

        static IEnumerable<Album> GetAlbums()
        {
            return new List<Album>
            {
                new Album { AlbumID = 1, Title = "Chikenfoot" },
                new Album { AlbumID = 2, Title = "Is There a Love in space[Joe Satriani]" }
            };
        }
    }
    class Album
    {
        public int AlbumID { get; set; }
        public string Title { get; set; }

        public override string ToString()
        {
            return String.Format("{0} {1}", AlbumID.ToString(), Title);
        }
    }
   
}

GetAlbums metodu IEnumerable<Album> tipinden bir referans döndürmektedir. Album, Object' in alt tipidir. Buna göre GetAlbums metodunun sonucunun IEnumerable<object> tipinden bir arayüze atanabiliyor olması düşünülebilir. Oysaki derleme zamanında aşağıdaki hata mesajı alınacaktır.

Görüldüğü üzere bir dönüştürme hatası alınmaktadır.

Şimdi buraya kadar anlattıklarımızı gözden geçirecek olursak belki şu cümleleri sarf edebiliriz.

  • Invariant tiplerin kullanıldığı yerlerde, belirtilen tipin birebir aynısının ele alınması gerekmektedir(.Net 4.0 öncesi generic koleksiyonlar)
  • Covaraint tiplerin kullanılabildiği yerlerde, alt tipten olan değişkenlerin parametre olarak aktarılması mümkündür.
  • Covariant' lık için güvensiz olma(Unsafe) durumlarıda söz konusudur.(Object tipli koleksiyonlar ve diziler)
  • Contravariant' lığa göre ise dönüş tipinin metoddan dönen tipin üst tipiden bir nesne örneğine atanması mümkündür.
  • Covariant tipler için geçerli olan güvensiz tip sendromu doğal olarak Contravariant tipler içinde söz konusudur.

Peki .Net 4.0 sonrasında...

.Net 4.0 versiyonunda Generic koleksiyonların katı olan tip kavramı bozularak Covariant ve Contravariant olmalarına izin verildiğini söyleyebiliriz. Ancak burada önemli bir fark olduğundan bahsetmemiz gerekiyor. Generic koleksiyonların Covariant ve Contravariant olarak çalışabilmeleri sağlanırken bunun güvenli olaraktan(Type Safe) gerçekleştirilebilmesi sağlanmış. Bu noktada örneğin object tipinden olan koleksiyonların covariance ve contravariance davranışlarından ayrıldığını ifade edebiliriz. Peki bu yeni kabiliyetler nasıl aktarılmış?

İşte IEnumerable<T> arayüzünün .Net 4.0' da tanımlanış şekli.

out T tanımlaması mutlaka dikkatiniz çekmiştir. Bu durum output safe olarak adlandırılmaktadır. Şimdi başka bir tipi göz önüne alalım.

Bu sefer in T kullanımı dikkati çekmektedir. Bu durum ise Input Safe olarak adlandırılmaktadır. Her iki durumda alt tarafta öyle ele alınmaktadırki generic koleksiyonlarda Covariant ve Contravariant' lığın tip güvenli olarak ele alınması mümkün olmaktadır. Sonuç olarak kafamız karışsada biraz .Net 4.0 ile birlikte generic koleksiyonların tip güvenli olaraktan Covariant ve Contravariant özellik gösterebilmelerinin mümkün hale geldiğini söyleyebiliriz. Bununla birlikte out T ve in T kullanımlarının şu an için sadece generic koleksiyonlar ve temsilcilerde(delegate) söz konusu olduğunu belirtelim. Dolayısıyla generic temsilcilerinde tip güvenli Covariant veya Contravariant olarak ele alınmaları mümkündür. Buna göre out T ve in T için şu ifadeleri kullanabiliriz.

  • out T Covariant tip kullanımını sağlamaktadır. Buna göre örneğin IEnumerable<object> tipte parametre alan bir metoda, IEnumerable<Product> gibi bir referansın atanabilmesi mümkündür.
  • in T Contravariant tip kullanımı sağlamaktadır. Buna göre örneğin IEnumberable<string> dönen bir metodun sonucunun IEnumerable<Object> tipine atanması mümkündür.

Dolayısıyla aşağıdaki örnek kod parçası sorunsuz olarak derlenecek ve çalışacaktır.

using System.Collections.Generic;

namespace NowInNet4
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Product> products = new List<Product>
            {
                new Product{ProductId=1,Name="Americano Coffee",ListPrice=10},
                new Product{ProductId=2,Name="English Royal Tea",ListPrice=12}               
            };

            Process(products);

            IEnumerable<object> allProducts = GetProducts();
        }

        static IEnumerable<Product> GetProducts()
        {
            return new List<Product>
            {
                new Product{ProductId=1,Name="Americano Coffee",ListPrice=10},
                new Product{ProductId=2,Name="English Royal Tea",ListPrice=12}               
            };
        }

        static void Process(IEnumerable<object> parameters)
        {
            // Bir takım işlemler
        }
    }
    class Product
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public decimal ListPrice { get; set; }
    }
}

Ben en azından biraz olsun anlamış durumdayım. Umarım sizlerede en iyi şekilde aktarabilmişimdir. Tekrardan görüşünceye dekhepinize mutlu günler dilerim.

CoContraVariance.rar (38,87 kb)

C# 4.0 ile Code Contracts

Cuma, 18 Aralık 2009 14:40 by bsenyurt

Merhaba Arkadaşlar,

Microsoft gibi dev yazılım firmalarının araştırma geliştirme ekipleri ve labarotuvar çalışmaları her zaman ilgimi çekmiştir. Herhalde pek çok yazılımcının hayalleri arasında bu tip firmalarda çalışmak ve yeni fikirleri ortaya atarak diğer yazılımcılara sunmak yer almaktadır. Microsoft' un DevLabs isimli portalında bu tip fikirlerin labarotuvar çalışmalarının yer aldığını görebilirsiniz. Örneğin son zamanların popüler konularından birisi olan observable koleksiyonları kullanarak asenkron(Asynchronous) ve olay güdümlü(Event Based) programlamayı kolaylaştıran Reactive Extensions, yeni başlayanlara yazılım anlatan Small Basic yada White Box testleri için geliştirilen Pex... Tabi daha pek çok labarotuvar çalışması yer almaktadır. Bunlardan biriside Code Contracts' tır.

Uzun süredir ilgimi çeken ama fırsat bulamadığım konulardan birisidir Code Contracts. Özellikle test süreçlerinde önem arz eden ve kodun çalışma zamanında veya kodlama zamanında varsayımsal bazı koşulları sağlayıp sağlamadığını tespit etmemizi sağlayan bir yenilik olarak düşünülebilir. Bu noktada kodun ön koşullu(Pre-Conditions) veya son koşullu(Post-Conditions) olarak test edilmesinin mümkün olduğunu söyleyebiliriz. Bu iki yeteneğe ek olarak bir nesnenin durumunun(State) beklendiği gibi olmasının kontrolüde Object Invariant özelliği sayesinde gerçekleştirilebilmektedir. 

Normal şartlarda Microsoft.Diagnostics.Contracts isim alanı(Namespace) altında yer alan tipler ile kod sözleşmelerinin kullanılması mümkündür. Bu isim alanı .Net Framework 4.0 Base Class Library içerisinde doğrudan gelmektedir. .Net Framework 4.0 öncesi sürümlerde kullanmak istediğimizdeyse Microsoft.Contracts.dll assembly' ının projeye referans edilmesi(örneğin XP işletim sisteminde C:\Program Files\Microsoft\Contracts\PublicAssemblies\v3.5 adresinde yer almaktadır) gerekmektedir. Kolay kullanım için yazıyı hazırladığım tarih itibariyle DevLabs' ten gerekli aracın(Tool) indirilerek kurulmasında yarar vardır. Code Contracts, Microsoft DevLabs içerisine dahil edilmiş önemli projelerden birisidir. Code Contracts' ın bloğun yazıldığı tarih itibariyle iki farklı versiyonu bulunmaktadır. Standart versiyon çalışma zamanı kontrollerini(Runtime Checking) yapabilmekteyken, Team System için üretilen versiyon static kontroller(Static Checking) yapabilmektedir. İlgili sürümler yine Visual Studio 2010 Beta 2 sürümü ile de çalışmaktadır.

Not : Eğer elinizde benim gibi Visual Studio 2010 Ultimate Beta 2 sürümü var ise, Team System Edition sürümünü kurabilirsiniz.

Kurulum işlemi sonrasında Visual Studio Ultimate 2010 Beta 2 üzerinde oluşturulan projelerin özelliklerine(Properties) aşağıdaki ekran görüntüsünde yer alan bir ara birimin dahil edildiğini görürüz.

,

Burada yer alan detaylı özellikleri zaman içerisinde öğreniyor olacağız. İlk etapta Static Checking kısmının sadece Team System destekli Visual Studio' lar üzerinde(Visual Studio 2008 içinde geçerlidir) etkinleştirilebildiğini belirtelim. Aslında bu kabiliyetlerin özellikle çalışma zamanında yapılan if...try...catch gibi kontrollerden ne gibi farkı olduğunu kavramamız oldukça önemlidir. Örneğin dökümantasyon avantajları bunlardan birisidir. Söz gelimi metod parametrelerinin gereklilikleri(Requirements), olası istisnalar(Exceptions) ve gerekli izinler(Permissions) için otomatik dökümantasyon üretimi sağlanabilmektedir. Diğer yandan Unit Test tarafına getirdiği avantajlardan da bahsedilebilir. Daha anlamılı Unit Test' lerin üretilebilmesi, özellikle her sözleşmenin(Contracts) bir analist gibi davranması ve çalıştırılan Test için pass/fail işaretlemesini sağlayabilmesi vb...Tabiki konuyu daha iyi kavrayabilmek adına bol bol örnek geliştirmekte yarar vardır. İşe çok basit bir Hello World uygulaması ile başlamakta yarar olacağı kanısındayım. Bu nedenle aşağıda yer alan Console uygulaması kodlarını yazdığımızı varsayalım.

using System.Diagnostics.Contracts;

namespace CodeContracts
{
    class Program
    {
        static void Main(string[] args)
        {
            ChinookContext context = new ChinookContext();       
            context.CreateAlbum(null, 1);
            Album result=context.CreateAlbum("The Best", 1);
            Contract.Ensures(result.Name.Length > 10, "Album nesnesinin oluşturulmasında albüm adının 10 karakterden fazla olması beklenir");
        }
    }

    class ChinookContext
    {
        public Album CreateAlbum(string albumName, int albumId)
        {
            Contract.Requires(!string.IsNullOrEmpty(albumName),"Album nesnesinin oluşturulması için Album adının null veya boş olmaması gerekir");
            Album albm=new Album {
                AlbumId=albumId
                ,Name=albumName };           
            return albm;
        }
    }

    class Album
    {
        public int AlbumId { get; set; }
        public string Name { get; set; }
    }
}

Bu örnek kod parçasında Album isimli sınıfa ait iki nesne örneği üretiminin gerçekleştirildiğini görmekteyiz. CreateAlbum isimli metod içerisinde Contract.Requires isimli bir static fonksiyon çağrısı olduğu hemen dikkatinizi çekmiş olmalıdır. Bu metod ile bir ön koşul(Pre-Condition) belirtilmektedir. Bu koşula göre CreateAlbum metoduna gelen albumName parametresinin değerinin null veya boş olmaması beklenmektedir. Diğer yandan Main metodunun son satırında da Contract.Ensures isimli bir metod çağrısı yer almaktadır. Bu çağrı ilede bir son koşul(Post-Condition) tanımlaması yapılmaktadır. Bu koşula göre CreateAlbum metodu ile üretilen ikinci Album nesne örneğinin Name özelliğinin karakter sayısının 10' un üzerinde olması istenmektedir.

Örneği bu haliyle çalıştırdığımızda hiç bir sorun olmadığını görürüz. Hımmm...Enteresan bir durum.Undecided Oysaki Requires veya Ensures metod çağrılarından en az birisine takılmamız gerekirdi. Aslında sorun henüz kod sözleşmelerinin çalışma zamanı(runtime) veya static olarak izlenmesi gerektiğini belirtmemiş olmamız. Bunun için Tool ile birlikte projeye eklenen Contracts sekmesindeki Runtime Checking kutusunu işaretlememiz yeterlidir.

Şimdi örneğimizi çalıştırdığımızda ilk olarak aşağıdaki mesaj kutusu ile karşılaştığımızı görürüz.

Dikkat edileceği üzere Album adının null geçilmesi nedeniyle bir uyarı mesajı üretilmiştir. Bir başka deyişle ön koşulun sağlanamadığı açık bir şekilde görülmektedir. Bu noktadan sonra hatayı görmezden gelip ilerleme şansımız vardır. Ignore düğmesine basarak devam ettiğimiz takdirde bu kez Post-Condition' a takıldığımızı görebiliriz.

Üretilen uyarı mesajına göre ikinci Album nesnesinin adının karaketer uzunluğunun istenildiği gibi olmadığı gözlemlenmektedir. Şimdi dilerseniz Object Invariant konusuna dair basit bir örnek geliştirmeye çalışarak devam edelim. Invariant özelliğini bir nesnenin istemci tarafından ele alındığı yerdeki durumunun(State) iyi olması ile alakalı bir yetenek şeklinde düşünebiliriz. Bir nesnenin durumunu içerdiği alanlar ifade ettiğinden, bu alanların beklenen şekilde olması iyi bir nesne(good object) olduğu anlamına da gelecektir. İşte örnek kod parçası.

using System.Diagnostics.Contracts;

namespace CodeContracts
{
    class Program
    {
        static void Main(string[] args)
        {
            Product bardak = new Product(1000,"Bardak",3.45);
        }
    }

    class Product
    {
        private int ProductId;
        private string Name;
        private double ListPrice;

        public Product(int pId,string pName,double pListPrice)
        {           
            ProductId = pId;
            Name = pName;
            ListPrice = pListPrice;
        }

        [ContractInvariantMethod]
        protected void InvariantCheck()
        {
            Contract.Invariant(this.ProductId > -1,"Ürün numarası pozitif değer olmalıdır");
            Contract.Invariant(this.Name.StartsWith("PRD-"),"Ürün adları PRD- ile başlamalı");
            Contract.Invariant(this.ListPrice != 0, "Liste fiyatı 0 olamaz");
        }
    }
}

Product isimli sınıfta ContractInvariantMethod niteliği(attribute) ile imzalanmış bir metod bulunmaktadır. Geriye değer döndürmeyen ve protected olarak işaretlenmiş bu metod içerisinde dikkat edileceği üzere Contract sınıfının static Invariant metoduna yapılan bazı çağrılar bulunmaktadır. Bu çağrılar içerisinde, üretilen Product nesnesinin durumunu simgeleyen alanların değerleri kontrol edilmektedir. Örnek çalıştırıldığında aşağıdaki mesaj ile karşılaşılacaktır.

Bu son derece doğaldır nitekim ürün adı Invariant çağrısında olduğu gibi PRD- ön eki ile başlamamaktadır. Dolayısıyla nesne örneği istenilen duruma sahip değildir.

Kişisel Not: Invariant kontrolünde dikkat edilmesi gereken iki durum vardır. Auto Property' ler kullanıldığında çalışma zamanında bir istisna mesajı alınacaktır. Bu istisna Contract.Invariant çağrılarının yapıldığı yerde gerçekleşecektir. Bunun sebebi Invariant çağrılarının null değer içeren özellikleri kontrol etmeye çalışmasıdır. Söz konusu durum varsayılan yapıcı metodlar kullanıldığında da nüksetmektedir. Söz gelimi yukarıdaki kod parçasında Product tipi için varsayılan yapıcı metod yazılıp buna göre bir Product nesnesi örneklendiğinde aşağıdaki çalışma zamanı istisna(Runtime Exception) mesajı ile karşılaşılır.

Name alanı string tipten olduğu için nesnenin ilk örneklenmesi sırasında varsayılan yapıcı metod(Default Constructor) tarafından null değer ile beslenir. Bunun sonucu olarakta Invariant metodu null değer üzerinde kontrol yapmaya çalışmaktadır.

Elbette Code Contracts konusu burada anlatıldığı kadar yalın ve sade değildir. Aksine dökümantasyonuna bakıldığında çok fazla kuralı olduğu görülmektedir. Özellikle Static Checking özelliği derleme işlemi sırasında bazı kod sözleşmelerinin kontrolü sağlamaktadır. Konuyu araştırdıkça ve öğrendikçe sizlere daha fazlasını aktarmaya çalışıyor olacağım. Tekrardan görüşünceye dek hepinize mutlu günler dilerim. 

CodeContracts.rar (25,40 kb)

C# 4.0 - COM Interop İyileştirmelerinden Dynamic Import ve Ommiting Ref [Beta 2]

Salı, 15 Aralık 2009 09:30 by bsenyurt

Merhaba Arkadaşlar,

Hani bazen insanın canı şöyle çıtır çıtır kuruyemiş çeker ya...Hatta çoğunlukla bir film seyrederken, maç izlerken, arkadaşları ile sohhet ederken, internette surf yaparken iyi gider ya...Hatta birisinin blog yazısını okurken kuruyemişleri yerken daha bir heyecanlı, istekli olur ya... Laughing İşte bende bu düşünceyle yola çıkıp siz değerli okurlarım kuruyemiş yerken kısa zamanda bir şeyler öğrenebilin, keyifli bir on dakika geçirin diye bu yazıyı hazırladım. Bakalım bu yazımızda bizleri hangi macera bekliyor.

Bildiğiniz üzere C# 4.0 ile birlikte yine köklü dil değişiklikleri hayatımıza girmiş bulunmakta. Özellikle dinamik diller ile olan etkileşimin arttırılması ve COM dünyası ile olan haberleşmede getirilen yenilikler son derece önemli. Bu gelen yenilikler arasında dynamic anahtar kelimesi, opsiyonal ve isimlendirilmiş parametrelerde(Optional & Named Parameters) en çok göze çarpanlar arasında yer almakta. Ancak çok fazla irdelenmeyen fakat özellikle COM Interop dünyasını ilgilendiren minik ve önemli iyileştirmelerde bulunmakta. Bu neden bu kısa yazımızda söz konusu minik iyileştirmelerden ikisini çok basit olarak incelemeye çalışıyor olacağız.

Kişisel Not : Bu konuda en doğru bilgilere ve güncel örneklere yazının hazırlandığı tarih itibariyle MSDN sitesinden ulaşabileceğinizi de hatırlatmak isterim.

Dynamic Import;

Dynamic anahtar kelimesi yardımıyla static olmayan ve COM, dinamik diller gibi ortamlardan gelen nesnelerin ele alınabilmesi mümkün hale gelmektedir. Peki COM Interop ile olan etkileşimde Dynamic tiplerin çaktırmadan geldiğini biliyor muydunuz? Dilerseniz konuyu irdelemek için aşağıdaki kod parçasını göz önüne alalım.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Excel = Microsoft.Office.Interop.Excel;

namespace COMInteropFeatures
{
    class Program
    {
        static void Main(string[] args)
        {
            var excelApp = new Excel.Application();
            excelApp.Workbooks.Add();
            excelApp.Visible = true;

            #region DynamicImport özelliği olmadan önce

            ((Excel.Range)excelApp.Cells[1, 1]).Value2 = "ID";

            Excel.Range range12 = (Excel.Range)excelApp.Cells[1, 2];
            range12.Value2 = "Phone Number";

            #endregion
        }
    }
}

Bu örnekte Excel API' sine ulaşılaraktan bir Workbook oluşturulması ve aşağıdaki görüntünün üretilmesi sağlanmaktadır.

Üzerinde önemle durmamız gereken nokta ise 1,1 ile 1,2 koordinatlarındaki hücrelere veriyi nasıl yazdığımızdır. Dikkat edilecek olursa 1,1 hücresine yazı yazmak için Range tipine bir dönüştürme işlemi yapılmıştır. Bu dönüşüm işlemi sonrasında Value2 özelliğine ulaşılabilmiştir. Devam eden kod satırında ise önce Range tipine dönüştürme ve atama işlemi yapılmış, sonrasında Value2 özelliğine gidilmiştir. Bu açıdan bakıldığında bir dönüştürme işlemi yapılmasının kaçınılmaz olduğu görülmektedir. Elbette bu eskiden böyleydi. Artık dynamic tipinin COM Interop nesneleri içerisine serpiştirildiğini görmekteyiz. Aşağıdaki görüntü bu durumu son derece iyi açıklamaktadır.

Görüldüğü gibi Cells üzerinden ulaşılan tip dynamic olarak ele alınmaktadır. Buna göre yukarıdaki kod parçası dynamic import kabiliyeti sayesinde dönüştürme işlemlerine gerek duyulmadan aşağıdaki gibi yazılabilir.

excelApp.Cells[1, 1].Value = "ID";

Excel.Range range12_ = excelApp.Cells[1, 2]; // Cast yapılmadan doğrudan atama işlemi
range12_.Value2 = "Phone Number";

Sonuç aynı olacaktır. Dikkat edilmesi gereken nokta herhangibir cast işlemi yapılmasına gerek olmayışıdır. Diğer yandan dynamic olan tiplerin çalışma zamanında çözümlenmesi söz konusu olduğundan aşağıdaki durumda bir handikap olarak görülebilir.

Cells[1,2]. sonrasında kullanabileceğimiz metodları gören, bilen, hatırlayan var mı? Undecided

Omitting Ref;

Yine COM Interop nesneleri ile olan münasibetlerimizde yaşadığımız sorunlardan biriside ref tipinden parametrelerin aktarılması için mutlaka geçici de olsa değişken tanımlamaları yapılması gerekliliğidir. Durumu daha net anlayabilmek için aşağıdaki kod parçasını göz önüne alalım.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Word = Microsoft.Office.Interop.Word;

namespace COMInteropFeatures
{
    class Program
    {
        static void Main(string[] args)
        {
            #region omitting ref

            #region öncesi

            Word.Application wordApp = new Word.Application();
            wordApp.Visible = true;
            object filePath = Environment.CurrentDirectory+"\\Belge.docx";
            object missing = Type.Missing;

            wordApp.Documents.Open(ref filePath, ref missing, ref missing, ref missing, ref missing, ref missing,  ref missing, ref missing, ref missing, ref missing, ref missing,  ref missing, ref missing, ref missing, ref missing, ref missing);

            #endregion

            #endregion
        }
    }
}

Bu seferki örneğimizde çok basit olarak Word Interop nesnesini kullanarak Belge.docx isimli dosyanın açılması sağlanmaktadır. Ancak Open metodunun yazılışı mutlaka dikkatinizi çekmiştir. Peki bir sürü ref missing yazmamış dışında bir sıkıntı görebiliyor musnuz? Wink Aslında Named ve Optional Parametre özellikleri ile bu kod stilinden zaten kurtulduk. Ne varki buradaki sıkınta bu değil. Sıkıntı, ref tipinden olan parametreler için missing isimli object tipinden bir değişken tanımlamak zorunda olmamız. Bu birden fazla çeşitte ref parametresi alan bir COM Interop çağrısı için birden fazla geçici değişken tanımlamak zorunda kalabiliriz anlamına da gelmekte. İşte C# 4.0 ile gelen Omitting Ref(ref' leri göz ardı etmek olarak düşünebiliriz) kabiliyeti sayesinde artık ref olarak kullanılması gereken parametrelere değer türü(Value Type) şeklinde argüman geçirebilmekteyiz. Peki ref kullanımından kaçılıyor mu? Elbetteki hayır. Arka planda derleyici bizim için gerekli geçici değişkenleri zaten oluşturuyor ve metod yine referans tipinden gelen parametreler ile çalışıyor. Kısacası yukarıdaki kodu aşağıdaki şekilde yazmamız mümkün.

wordApp.Documents.Open(filePath, Type.Missing, Type.Missing);

Dikkat edileceği üzere doğrudan değer ataması yapılmış, herhangibir değişken kullanımına gidilmemiştir.

İşte sizlere bir kaç dakika içerisinde çerez niyetine okuyup öğrenebileceğiniz bir yazı. Umarım faydalı olmuştur. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

.Net 4.0 - Lazy Initialization [Beta 2]

Pazartesi, 14 Aralık 2009 10:30 by bsenyurt

Merhaba Arkadaşlar,

Eminim hepimiz arada sırada tembellik yapıyor ve ilk bulduğumuz rahat köşeye kıvrılıp hiç bir şeyi düşünmeden rahatça uyuyabiliyoruz. Eğer bulunduğumuz yer uyumaya çok müsait değilse yandaki kedi gibi ortama ayak uydurup yinede uyuyoruz Smile Siz hiç gözleri açık uyuyabilen insanlar gördünüz mü? Bunun adı düpe düze tembellik olabiliyor bazen. İhtiyaç dışında uyumak ve hiç bir şeyle uğraşmadan öylece kala kalmak tembelliğin doruk noktaya ulaştığı anlar olarak düşünülebilir. Ama gelin görün ki, programatik ortamda da nesnelerin zaman zaman tembellik etmesi gerekmektedir. Bugünkü yazımızda bu konuyu değerlendiriyor olacağız. Peki neymiş şu Lazy Initialization bir bakalım.

Lazy Initialization yetenekleri sayesinde programların gereksiz bellek tüketimlerinin önüne geçilebilir ve ayrıca performans kazanımı sağlanabilir. Aslında uygulamalarımızda Lazy Initialization kullanmamız için gerek ve yeter iki sebep bulunmaktadır. İlk olarak üretilme maliyetleri yüksek olan nesnelerin tanımlandıkları anda oluşturulmaları yerine, kullanılmaya başladıkları yerde oluşturulmaları gerektiği durumlarda tercih edilebilir. Bu noktada aklımıza LINQ içerisinde zaten var olan Deferred Execution özelliği gelmektedir ki Lazy Initialization yeteneklerine sahip .Net CLR tipleride tam olarak bunu sağlamak üzere tasarlanmıştır. İkinci bir sebep olarak, maliyeti yüksek olan bazı hesaplamaların tamamlanmasından sonra ilgili nesnelerin oluşturulması istenebilir. Söz gelimi uygulamamın çalıştırılması ile üretilen n sayıda nesneden sadece gerekli olanların üretilmesi istendiği durumlarda Lazy Initialization yeteneklerinden yararlanılabilir.

Çok doğal olarak nesnelerin ihtiyaç duyuldukları anda oluşturulmasının sağlanması için kendi tekniklerimizi de geliştirebiliriz. Bu noktada Reflection' ın büyük önemi olduğunu vurgulamak gerekir. Nitekim nesnelerin oluşturulmasI işlemini üstlenecek bir Wrapper' ın mutlaka tasarlanmış olması ve hatta içerisinde Instance üretimi için gerekli kodlamaların yapılması gerekmektedir. Activator sınıfının çalışma zamanında nesne üretimi ile ilişkili çeşitli static metodları bu amaçla kullanılabilir. Ancak .Net Framework 4.0 ile birlikte Lazy Initialization için kullanılabilecek Lazy<T> sarmalayıcı sınıfı gelmektedir. Bu tip sayesinden nesnelerin gerek duyulduğu zamanlarda oluşturulmasının sağlanması son derece kolaylaştırılmaktadır. Üstelik Lazy<T> Thread Safe bir tip olarak kullanılabilmektedir.

Dilerseniz konuyu biraz daha net kavramak adına basit bir örnek geliştirelim. Örnek senaryomuza göre program içerisinde ele alınan bir oyun sahnesi ve bu sahne içerisinde yer alan grafiksel şekillerin tutulduğu çeşitli nesnelerin üretim işleminde Lazy Initialization tekniklerinin nasıl kullanıldığını incelemeye çalışacağız. Bu senaryoda Lazy Initialization kullanmamız için öne sürdüğümüz sebep ise şu olacak; programın ilerleyen adımlarında pek çok farklı sahne ve bu sahne içerisinde yer alan grafik nesnelerin yeri geldiğinde oluşturulması ve böylece programın başlangıcında n sayıda sahne için yapılacak olan oluşturulma maliyetinin azaltılması. Bu amaçla ilk olarak Visual Studio 2010 Ultimate Beta 2 üzerinde aşağıdaki tiplere sahip bir Console uygulaması geliştirdiğimizi düşünelim.

using System.Collections.Generic;

namespace BeLazy
{
    enum ActorType
    {
        Player,
        Computer,
        Wall,
        Enemy
    }
    class Actor
    {
        public int ActorId { get; set; }
        public string Title { get; set; }
        public string Capability { get; set; }
        public ActorType ActorType { get; set; }
        public byte[] Image { get; set; }

        public override string ToString()
        {
            return string.Format("Actor Id {0} Title {1} Capability {2} Actor Type {3}", ActorId.ToString(), Title, Capability,ActorType.ToString());
        }
    }
    class Scene
    {
        public int SceneId { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
        public byte[] Background { get; set; }
        public List<Actor> Actors { get; set; }

        public override string ToString()
        {
            return string.Format("SceneId {0} Widht {1} Height {2}", SceneId.ToString(), Width.ToString(), Height.ToString());
        }
    }
}

Tamamen hayali olarak tasarlanmış olan bu tiplerde ana fikir Scene tipi içerisinde bir Actor listesinin olmasıdır. Her iki tip içerisinde byte[] tipinden özellikler yer almaktadır. Scene nesnelerinin sayısının çok fazla olduğu bir durumda, tümünün bir seferde oluşturulması bellek üzerindeki tüketimi arttırabileceği gibi program performansınıda olumsuz yönde etkileyebilir. Bu nedenle Lazy Initialization tekniklerinden faydalanarak sadece gereksinim duyulduğu yerde oluşturulma işlemi yolu tercih edilebilir.

Peki oluşturulma işlemi hangi aşamada gerçekleşmektedir? Bu noktada Lazy<T> tipinin Value özelliği devreye girer. Value özelliğinin kullanıldığı ilk yerde T tipinin oluşturulma işlemi başlamaktadır. Şimdi bu durumu analiz etmek amacıyla Program kodunun içeriğini aşağıdaki gibi yazdığımızı düşünelim.

using System;

namespace BeLazy
{
    class Program
    {
        static void Main(string[] args)
        {
            Lazy<Scene> lazyScene = new Lazy<Scene>();

            Console.WriteLine("Scene oluşturuldu mu? {0} ",lazyScene.IsValueCreated);
            lazyScene.Value.SceneId = 10;
            Console.WriteLine("Scene oluşturuldu mu? {0} ", lazyScene.IsValueCreated);
            Console.WriteLine("Scene ID : {0}",lazyScene.Value.SceneId.ToString());
        }
    }
}

İlk olarak Lazy<Scene> tipinden bir nesne örneği oluşturulmaktadır. Sonrasında IsValueCreated özelliği yardımıyla Scene nesnesinin oluşturulup oluşturulmadığına bakılır. Bir sonraki satırda dikkat edileceği üzere Value özelliği üzerinden gidilerek SceneId için değer ataması yapılmıştır. Uygulamanın çalışma zamanı çıktısı aşağıdaki gibidir.

Volaaaa!!! Cool Görüldüğü üzere Value özelliği çağırılana kadar IsValueCreated özelliği false değer döndürmektedir. Bir başka deyişle söz konusu nesne henüz oluşturulmamıştır. Ancak Value özelliğinin kullanılması ile birlikte bir Scene nesnesinin örneklendiği görülmektedir. Burada dikkat edilmesi gereken noktalardan biriside Value özelliğinin Readonly olmasıdır. Bir başka deyişle Value özelliği ile elde edilen bir nesne referansına yeni bir atama gerçekleştirilemez.

Ama tabiki Value üzerinden gidilen tipin özellikleri değiştirilebilir. Örneğimizde dikkat çekici noktalardan bir diğeri de Scene nesnesinin aslında karmaşık bir tip olarak içerisinde başka tipleride barındırıyor olmasıdır. Buna göre Value özelliğinden yararlanılarak diğer tiplerin değerlerinin de atanması sağlanabilir...Ya da...Ya da aşağıdaki kod parçasında olduğu gibi tembel bir üretim gerçekleştirilebilir.

using System;
using System.Collections.Generic;

namespace BeLazy
{
    class Program
    {
        static void Main(string[] args)
        {
            Lazy<Actor> azman = new Lazy<Actor>(() =>
                new Actor {
                     ActorId=1,
                      ActorType= ActorType.Computer,
                       Capability="Çoook!",
                        Image=new byte[1000],
                         Title="Azman"
                }
                );

            Lazy<Actor> gazman = new Lazy<Actor>(() =>
                new Actor
                {
                    ActorId = 1,
                    ActorType = ActorType.Enemy,
                    Capability = "Yüksek Gaz depolama kapasitesi!",
                    Image = new byte[5000],
                    Title = "Gazman"
                }
                );

            Lazy<Scene> scene = new Lazy<Scene>(() =>
                new Scene
                {
                    SceneId = 1001,
                    Background = new byte[10000],
                    Height = 100,
                    Width = 250,
                    Actors = new List<Actor> { azman.Value, gazman.Value }
                }
                );

            Console.WriteLine("Scene oluşturuldu mu? {0} ",scene.IsValueCreated);
            Console.WriteLine("Azman oluşturuldu mu= {0}",azman.IsValueCreated);
            Console.WriteLine("Gazman oluşturuldu mu= {0}", gazman.IsValueCreated);
           
            Scene currentScene=scene.Value;

            Console.WriteLine("Scene oluşturuldu mu? {0} ", scene.IsValueCreated);
            Console.WriteLine("Azman oluşturuldu mu= {0}", azman.IsValueCreated);
            Console.WriteLine("Gazman oluşturuldu mu= {0}", gazman.IsValueCreated);     

            Console.WriteLine(currentScene.ToString());

            foreach (Actor actor in currentScene.Actors)
            {
                Console.WriteLine("\t {0}",actor.ToString());
            }
        }
    }
}

Buna göre çalışma zamanı içeriği aşağıdaki gibi olacaktır.

Görüldüğü üzere Scene ve Actors özelliğinin işaret ettiği koleksiyon içerisindeki tüm Actor tipleri Lazy olarak üretilmektedir. Üretim noktası ise currentScene değişkenin Value özelliği ile atamanın yapıldığı satırdır. Bu satırdan sonra Scene nesnesi ve içeriğindeki Actor nesnelerinin oluşturulması gerçekleştirilmektedir. Tabi istenirse Actor oluşturma işlemleride sonraki bir adıma bırakılabilir.

İlginç olan bir noktada, Lazy<T> tipinden özelliklerin(Type Property) tanımlanabilmesidir. Dilerseniz aşağıdaki örnek kod parçasını göz önüne alalım.

using System;
using System.Collections.Generic;

namespace BeLazy
{
    class Program
    {
        static void Main(string[] args)
        {
            Contact cntc = new Contact(1, "Burak Selim Şenyurt", "New York", "USA", "1000");
            Console.WriteLine("Contact içerisindeki Address nesnesi üretilmiş mi ? {0}", cntc.IsAddressCreated);
            Console.WriteLine("{0} {1} {2}",cntc.Address.Country,cntc.Address.City,cntc.Address.PostalCode);
            Console.WriteLine("Contact içerisindeki Address nesnesi üretilmiş mi ? {0}", cntc.IsAddressCreated);
        }
    }

    class Contact
    {
        private Lazy<Address> _address;

        public Contact(int contactId,string name,string city,string country,string postalCode)
        {
            _address = new Lazy<Address>(
                () => new Address {
                     City=city,
                     Country=country,
                     PostalCode=postalCode
                }
                );

            ContactId = contactId;
            Name = name;
        }

        public Address Address
        {
            get
            {
                return _address.Value;
            }
        }
        public bool IsAddressCreated
        {
            get
            {
                return _address.IsValueCreated;
            }
        }
        public int ContactId { get; set; }
        public string Name { get; set; }
       
    }

    class Address
    {
        public string City { get; set; }
        public string Country { get; set; }
        public string PostalCode { get; set; }
    }
}

Örnekte yer alan Contact sınıfı içerisinde Lazy<Address> tipinden private bir alan tanımlandığı görülmektedir. Bu alanın oluşturulması Contact sınıfının yapıcı metodu içerisinde gerçekleştirilmektedir. Address isimli özellik ilede, Lazy<Address> tipinden olan _address alanının Value özelliğinin değeri geriye döndürülmektedir. Zaten Value özelliğine referans atanması gerçekleştirilemediğinden Address isimli özellik sadece get bloğuna sahip olacak şekilde tanımlanmıştır(Read Only). Bu kodlamaya göre Contact sınıfına ait bir nesne örneklendikten sonra içerisinde yer alan Address nesnesi hemen oluşturulmaz. Ancak Contact sınıfının nesne örneği üzerinden Address özelliğine gidilir ve Value özelliğine ulaşılırsa örnekleme işlemi gerçekleşecektir. Bu durum çalışma zamanında daha net bir şekilde görülebilir.

Dikkat edileceği üzere Contact nesnesi örneklense bile Address nesnesi henüz oluşturulmamıştır. Ancak Address nesnesinin Value özelliğine ulaşıldığı ilk yerde bu işlem gerçekleşmektedir.

Lazy<T> tipinin kullanımında dikkatli olunması gereken noktalardan biriside Multi-Thread operasyonlardır. Özellikle birden fazla Thread' in aynı Lazy<T> nesne örneğini kullanması halinde ilgili nesnenin hangi Thread içerisinde oluşturulacağının bir önemi kalmamaktadır. Ancak oluşturulma işlemleri sırasında meydana gelecek istisnalarda(Exceptions) diğer Thread' lerinde otomatik olarak etkilenmesi söz konusudur. Buna ek olarak Lazy<T> tipleri oluşturulurken Thread Safe olup olmayacakları çok basit bir şekilde belirlenebilir. Bu durmu daha net bir şekilde anlayabilmek adına dilerseniz aşağıdaki kod parçasını göz önüne alalım.

using System;
using System.Collections.Generic;
using System.Threading;

namespace BeLazy
{
    class Program
    {
        static void Main(string[] args)
        {
            Lazy<Information> information = new Lazy<Information>();

            Thread threadA=new Thread(
                ()=>
                    {
                        Console.WriteLine(information.Value.ToString());
                        Console.WriteLine("\tThread A Thread Id : {0}",Thread.CurrentThread.ManagedThreadId.ToString());
                    }
                    );

            Thread threadB = new Thread(
                () =>
                {
                    Console.WriteLine(information.Value.ToString());
                    Console.WriteLine("\tThread B Thread Id : {0}", Thread.CurrentThread.ManagedThreadId.ToString());
                }
                    );

            Thread threadC = new Thread(
                () =>
                {
                    Console.WriteLine(information.Value.ToString());
                    Console.WriteLine("\tThread C Thread Id : {0}", Thread.CurrentThread.ManagedThreadId.ToString());
                }
                    );

            threadA.Start();
            threadB.Start();
            threadC.Start();

            threadA.Join();
            threadB.Join();
            threadC.Join();
        }
    }

    class Information
    {
        private int threadId;
        private string initializeTime;

        public Information()
        {
            threadId = Thread.CurrentThread.ManagedThreadId;
            initializeTime = DateTime.Now.ToLongTimeString();
            Thread.Sleep(2000);
        }

        public override string ToString()
        {
            return String.Format("Thread Id : {0} Time : {1}", threadId.ToString(), initializeTime);
        }
    }
}

Örneğimizde 3 farklı Thread' in çalıştırıldığı görülmektedir. Bu Thread' lerin tamamı kendi içlerinde Lazy<Information> tipinden olan information değişkenini kullanmaktadır. Buna göre Thread' lerden hangisi ilk olarak Value özelliğine erişirse, Information nesnesi örneklemiş olacaktır. Buna ek olarak çalışma zamanındaki sonuçlara bakıldığında tüm Thread' lerin ilk üretilen Information nesne örneğine ulaştığı bu nedenle Information yapıcı metodunda oluşturulan ManagedThreadId değerlerinin ve zamanların aynı olduğu görülür. İşte sonuçlar.

Bu senaryoya göre nesnenin hangi Thread içerisinde oluşturulduğunun hiç bir önemi yoktur. Şimdi akla şöyle bir soru gelebilir. Lazy<Information> örneği oluşturulurken Thread-Safe davranılacağı nerede belirtilmiştir?

Aslında Lazy<T> tipinin isThreadSafe parametresinin değeri varsayılan olarak true' dur ve istenirse yapıcı metod içerisinde değiştirilebilir. Özelliğe false değer atanması halinde genellikle ilgili nesnenin tek bir thread içerisinde kullanıldığı varsayılır ve bu azda olsa performans kazanımına neden olur. Ancak birden fazla thread' in aynı Lazy<T> nesnesini kullanacağı hallerde koordinasyon daha büyük önem kazanmaktadır ve bu nedenle isThreadSafe özelliğinin değerinin true olarak bırakılması önerilir.

Kişisel Not : Özellikle birden fazla Thread' in aynı nesnenin kendi içlerinde farklı kopyalarını alarak kullanmaları istendiği durumda .Net 4.0 ile gelen ThreadLocal tipinden yararlanılabilir ki .Net 3.5' te bunun için ThreadStatic isimli bir nitelikten yararlanılmaktadır. İşte size güzel bir araştırma konusu Laughing

Buraya kadar anlattıklarımıza göre dikkat edilmesi gereken bazı noktalar olduğuda aşikardır;

  • Lazy<T> içerisinde kullanılacak tipin mutlaka varsayılan yapıcı(Default Constructor) metodu olmalıdır. Nitekim oluşturma işleminde aslında Activator sınıfının CreateInstance metodu devreye girmektedir.
  • Value özelliği ReadOnly' dir. Bu nedenle kullanıldıktan sonra T tipinden bir referansın atanması gerçekleştirilemez.
  • Multi-Thread senaryolarda isThreadSafe özelliğini true olarak bırakmak doğru bir yaklaşımdır.
  • Single-Thread senaryolarda isThreadSafe özelliği performans kazanımı adına istenirse false bırakılabilir.
  • Value özelliğine erişildiğinde oluşabilecek bir istisna halinde kodun ilerleyen kısımlarında Value özelliğine giden her çağrı için aynı Exception örneği söz konusu olacaktır. Yani Value özelliğinin ilk çağırıldığı yerde oluşacak bir istisna devam eden Value çağrılarına da akacaktır. Bu Multi-Thread senaryolarda da isThreadSafe özelliğinin değeri true bırakıldığı sürece de geçerlidir.
  • Çekinilmesi gereken nokta şudur; isThreadSafe özelliğine false değer verilmesi halinde thread' ler arasında senkronizasyon ortadan kalkacağından, bir Thread' in exception gördüğü noktada diğer bir thread kullanılabilir bir Value özelliği ile karşılaşabilir. Aman dikkat.Sealed

Umarım faydalı bir yazı olmuştur. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

BeLazy.rar (28,31 kb)

C# 4.0 - ExpandoObject

Çarşamba, 21 Ekim 2009 16:14 by bsenyurt

Merhaba Arkadaşlar,

Bildiğiniz üzere .Net Framework 4.0 ile birlikte gelmesi muhtemel en köklü yenilikler arasında Dynamic Language Runtime alt yapısı yer almaktadır. Bu anlamda daha önceden dynamic anahtar kelimesini inceleyerek tiplerin dinamik olarak oluşturulup kullanılmasını kavramaya çalışmıştık. Bu yazımızda nasıl bir yenilikten bahsedeceğimizi anlatabilmek için öncelikle aşağıdaki kod parçasına odaklanmanızı istiyorum.

Not : Örnek henüz yayınlanmış olan Visual Studio 2010 Ultimate Beta 2 sürümü üzerinde geliştirilmiş bir Console uygulamasıdır.

Console.WriteLine("İlk Bakış\n");

dynamic employee = new ExpandoObject();

employee.Name = "Burak";
employee.Salary = 1000.23F;
employee.Birth = new DateTime(1976, 12, 4);
employee.WorkingArea = new ExpandoObject();
employee.WorkingArea.City = "Istanbul";
employee.WorkingArea.Degree = 1;
employee.WorkingArea.CustomerCount = 190;

Console.WriteLine(String.Format("\tName:{0} City:{1} Degree:({2})", employee.Name, employee.WorkingArea.City, employee.WorkingArea.Degree));

Şimdi bu kod parçası ile ilişkili olara bir kaç ipucu vermek istiyorum;

  1. Öncelikli olarak uygulamamızda Name,Salary ve diğer özelliklere sahip Employee gibi ismi olan herhangibir tip(type) tanımı bulunmamaktadır Wink
  2. İkinci kodu yazdığımız sırada employee isimli değişkene herhangi bir özellik eklemek istediğimizde aşağıdaki bilgi penceresi ile karşılaşırız.

Ahaaaa!!! Wink 

Sanıyorum olayın sizde farkına varmış durumdasınız. Aslında employee ismiyle tanımlamış olduğumuz değişkeni bir sınıf nesne örneği olarak inşa ettiğimizi düşünebiliriz. Ancak bunun için Emplyee tipini tanımlamış değiliz. Tamamen kodlama zamanında verdiğimiz bazı kararlar uygulamaktayız. Örneğin employee ismiyle tanımlanan tipin ve içeriğinin oluşturulması aşaması tamamen çalışma zamanına(Runtime) bırakılmış durumdadır. employee isimli değişkenin Name, Salary, Birth gibi özellikleri dışında başka bir ExpandoObject tipine işaret eden WorkingArea isimli bir özelliği daha bulunmaktadır. Buna göre WorkingArea özelliği de bir tip olarak düşünülebilir ki City, Degree, CustomerCount gibi özellikleri yer almaktadır.  Tabiki çalışma zamanında, özelliklere(Properties) atanan değerlere göre tiplerin ne olacağı da belirlenmektedir. Buna göre örneğin Name özelliğinin çalışma zamanında string tipinden olacağı açık ve nettir. Ancak WorkingArea özelliğinin kendiside aslında bir ExpandoObject tipidir. Kodun ilerleyen kısımlarına baktığımızda, employee değişkeninin özelliklerini kullanabildiğimizi de görebiliriz. Örnek kod parçamızın çalışma zamanı çıktısı aşağıdaki gibi olacaktır.

ExpandoObject tipinin aslında hangi amaçla kullanıldığını ilk etapta görmüş olduk. Peki daha neler yapabiliriz? Örneğin bir liste veya dizi oluşturulmasında, ExpandoObject nesnelerini kullanabilir miyiz? İşte cevabımız; 

Console.WriteLine("\nNesne Topluluğu\n");

dynamic personList = new List<dynamic>();

personList.Add(new ExpandoObject());
personList[0].Name = "Burak";
personList[0].Salary = 1000;

personList.Add(new ExpandoObject());
personList[1].Name = "Bill";
personList[1].Salary = 1250;

personList.Add(new ExpandoObject());
personList[2].Name = "Eva";
personList[2].Salary = 1050;

personList.Add(new ExpandoObject());
personList[3].Name = "Mayk";
personList[3].Salary = 900;

foreach (var person in personList)
    Console.WriteLine("\t{0}\t{1}", person.Name, person.Salary);

Bu sefer List<dynamic> tipinden bir koleksiyon oluşturulduğu ve koleksiyonun her bir nesnesinin ExpandoObject tipinden tanımlandığı görülmektedir. Buna göre örnek olarak Name ve Salary özellikleri olan nesnelerin dynamic listeye eklendiği görülebilir. Tabiki her öğe oluşturulma işleminden önce koleksiyona ExpandoObject tipinden bir nesne örneği eklenmesi gerekmektedir. Örneğin çalışma zamanındaki çıktısı aşağıdaki gibidir. 

Hatta istersek çalışma zamanında oluşturulacak bu nesneler üzerinde LINQ sorgularının çalıştırılmasını da sağlayabiliriz. Aşağıdaki kod parçasında yukarıdaki listenin bir LINQ ifadesi ile sorgulandığı görülmektedir.

Console.WriteLine("\nLINQ Kullanımı\n");

var result = from person in (personList as List<dynamic>)
    where person.Salary <= 1000
    select person;

foreach (var r in result)
    Console.WriteLine("\t{0}\t{1}", r.Name, r.Salary);

Bu kez dynamic liste içerisindeki nesnelerden Salary özelliği 1000 birime eşit ve az olanların çekilmesi için bir LINQ ifadesi kullanılmaktadır. Çalışma zamanındaki sonuç aşağıdaki gibi olacaktır.

Daha neler yapabilir? Acaba tanımladığımız ExpandoObject tipinden nesne için bir olay(Event) ekleyebilir miyiz? Hımmm...Undecided Bu oldukça ilginç olabilir. Aşağıdaki kod parçası ile bir deneyelim bakalım;

static void Main(string[] args)
{
   #region Event Kullanımı

   dynamic file = new ExpandoObject();

   file.Name = "DynamicOlmak.txt";
   file.Size = 1024;

   file.SizeChanged = null; // Bu satır olmadığı takdirde hata mesajı alınmakta.
   file.SizeChanged += new EventHandler(OnSizeChanged);
   EventHandler e = file.SizeChanged;
  
   Random rnd = new Random();
   file.Size += rnd.Next(1000, 10000);

   if (file.Size>=5000 && e != null)
      e(file, null);

   #endregion
}

public static void OnSizeChanged(object sender, EventArgs e)
{
   Console.WriteLine("Boyut değişti olayı tetiklendi");
}

Bu sefer file isimli değişkenin işaret ettiği tipe SizeChanged isimli bir olay(event) bildirimi yapılmaktadır. Event' ler bildiğiniz üzere delegate tipleri ile ilişkilidir. Örnekte basitlik açısından standart EventHandler temsilcisi kullanılmıştır ki dilerseniz farklı handler' ları veya kendi tanımladığınız delegate tiplerini de ele alabilirsiniz. Olayın file nesnesine eklenmesi sırasında birde olay metodunun işaret edildiği görülmektedir. Bildiğiniz üzere bu olay metodu, EventHandler temsilcisinin belirttiği parametrelere ve dönüş tipine sahip olmalıdır. Buna göre OnSizeChanged metodu olayın gerçekleşmesi sonrası devreye girecek olan fonksiyonumuzdur. Çok doğal olarak olayın bir sebepten tetikleniyor olması gerekir. Örnekte Size değerinin 5000' in üzerinde olması durumu değerlendirilmeye çalışılmıştır. Eğer böyle bir koşul oluşur ve e ile ifade edilen olay file nesnesine daha önceden ilave edilirse söz konu olay metodu tetiklenecektir. Aslında uygulamayı Debug ettiğimizde söz konusu olay metodu için MulticastDelegate kullanıldığını rahatlıkla görebiliriz.

Dikkat edileceği üzere dinamik olarak üretilen nesne içerisinde yer alan IDictionary koleksiyonunda Name, Size özellikleri ile birlikte MulticastDelegate tipinden tanımlanmış bir metod bildirimde yer almaktadır.

Peki ExpandoObject örneklerini metodlara parametre olarakta geçirebilir miyiz? Elbette Wink Aşağıdaki kod parçasını göz önüne alalım;

static dynamic file = new ExpandoObject();

static void Main(string[] args)
{
   #region Metoda Parametre Aktarımı

   file.Name = "DynamicOlmak.txt";
   file.Size = 1024;

   Console.WriteLine("Metod çağrısı öncesi dosya boyutu {0}",file.Size);
   Action(file);
   Console.WriteLine("Metod çağrısı sonrası dosya boyutu {0}", file.Size);

   #endregion
}

static void Action(dynamic obj)
{
   obj.Size += 456;
}

Burada file değişkeni sınıf seviyesinde tanımlanmıştır. Tabi örneği Console uygulamasında geliştirdiğimizden dynamic olarak tanımlanan ExpandoObject tipinin başına static anahtar kelimesininde gelmesi gerekmektedir. Bu kodu okurken biraz tuhaflığa neden olmaktadır. Undecided ExpandoObject örneği Action metoduna dynamic tipi üzerinden aktarılmaktadır. Dolayısıyla Action metodu içerisinde kullanılan obj değişkeninin file değişkeni olduğunu metod çağrısını yaptığımız yerde belirtmekteyiz. Bu nedenle Action metodu içerisine zaten file değişkeninin geldiğini biliyor olmalıyız. (Peki hangi tipin geldiğini anlayabilir miyiz? İşte size süper bir araştırma konusu Wink) Metod içerisinde sembolik olarak Size değerini arttırmaktayız. İşte çalışma zamanı sonucu; 

ExpandoObject tipi görüldüğü üzere dinamik olarak tiplerin oluşturulması, onlara üye tanımlanması(Özellik, Event gibi), özelliklerine değer atanması gibi işlemleri yapabilmemize olanak sağlamaktadır. Bu anlamda test senaryolarında stub veya moc nesneler için kullanılabilecek bir yenilik olarak düşünebiliriz belkide. Aklıma ilk gelen kullanım alanın bu olması, belkide The Art of Unit Testing kitabını okuyor olmamadan da kaynaklanabilir. Wink Aslında söz konusu yeniliği tanımak, kavramak ve gerçek hayat senaryolarındaki yerini anlamak için biraz daha araştırma yapmamız, çalışmamız gerektiği ortadadır. En azından benim...

Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

ConsoleApplication1.rar (25,51 kb)

C# 4.0 - Seçilebilen, İsimlendirilebilen Parametreler(Named and Optional Parameters), ref' i Görmezden Gelmek(Ommit Ref) ve PIA için Yenilikler

Pazartesi, 4 Mayıs 2009 22:46 by bsenyurt

Merhaba Arkadaşlar,

Bir önceki blog yazımızda C# 4.0 ile birlikte gelen önemli yeniliklerden birisi olan dynamic kavramına değinmeye çalışmıştık. Elbetteki C# 4.0 ile birlikte gelen başka yeniliklerde var. Bu yeniliklerde, diğerleri gibi belirli ihtiyaçlardan ortaya çıkmıştır. Öncelikli olarak bu ihtiyaçları ortaya koymaya çalışıyor olacağız. Bu nedenle PDC 2008'de dağıtılan Visual Studio 2010(PreBeta) sürümü ile yazdığım aşağıdaki kod parçasını bir süreliğine göz önüne alalım.

using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;

namespace NewFeatures2
{
    class Program
    {
        static void Main(string[] args)
        {
            Word.Application wrdApp = new Microsoft.Office.Interop.Word.Application();           
            wrdApp.Visible = true;
            object fileNamePath = @"C:\Yeni Ozellikler.docx";
            object missingValue = Missing.Value;

            wrdApp
                .Documents
                .Open(ref fileNamePath, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue);
            Console.WriteLine("Kapatmak için bir tuşa basınız");
            Console.ReadLine();
        }
    }
}

İlk olarak şunu belirtmek isterim; bu basit Console uygulamasında Microsoft.Office.Interop.Word assembly' ına ait bir referans yer almaktadır.

Program, sistemde yüklü olan Yeni Ozellikler.docx isimli bir Word dosyasını açmak için gerekli kodları içermektedir. Bu Word dosyasının açılması içinse, Microsoft.Office.Interop.Word assembly' ından yaralanılmaktadır. Bu assembly aslında Word ile konuşabilmemizi sağlayan COM API'sini sarmalayan(Wrap) bir yönetimli(Managed) kütüpanedir. Kod içerisinden, Word dökümanına erişebilmek için Application tipinden bir nesne oluşturulmaktadır. Uygulamanın görünürlüğü Visible özelliği ile set edildikten sonra ise, Open metodundan yararlanarak ilgili Word belgesinin açılması sağlanmaktadır. Ancak bu kod parçasında geliştirici açısından bazı zorluklar olduğu rahatlıkla gözlemlenebilir.

1- Open metoduna ait 16 adet parametrenin tamamının girilmesi zorunludur. Aşağıdaki şekilde durumun sıkıcılığı gözler önündedir.

NOT : Burada Juval Lowy' nin IDesign şirketinde kullandığı ve pek çok şirket tarafından benimsenen C# kodlama standartları aklıma geliyor. Buradaki belirttiği bir maddeyi hatırlıyorum. "Metodların argüman sayılarının 5 i geçmesinden kaçının. Eğer öyleyse struct tipini kullanın". Wink Burada bir COM API' sinin Wrap edilmiş kütüphanesi içerisindeki bir fonksiyonun parametre yapısının değiştirilemeyeceği fikri belleğimizi tamamıyla kaplayabilir. Tabi C# 3.0 sonrasında bir fırsat olabileceği de akıllara gelebilir. "Bir Extension metod yardımıyla Open metodu yerine bir alternatif geliştirebilir miyim? En azından parametre sayısını düşürmemizi kolaylaştıracak..." Bunu denemenizi öneririm.

2- Parametreler COM nesnesine iletildiğinden, dışarıdan yapılacak olan atamalarda object tipinin kullanılması gerekmektedir.

3- ref anahtar kelimesinin kullanılması zorunludur.

Gerçekte, Open metodu içerisinde işimize yarayan ve bizim için anlam ifade eden tek bir parametre yer almaktadır. O da açılmak istenen dosyanın adıdır. Diğer parametrelerinin hiçbirini kullanmadığımız halde yazmak zorunda olduğumuzu görüyoruz. Keşke sadece gerekli olanları yazsabilseydik; o zaman bu iş daha kolay olmazmıydı? Frown Nitekim buradaki Open metodu haricinde, çok daha fazla sayıda argüman ile çalışabilen COM fonksiyonellikleri söz konusu olabilir. Böyle bir durumda tam olarak tüm parametreleri yazma zorunluluğu bir kenara dursun, bunların bütünün ne işe yaradığınında bilinmesi gerekir.

Sanırım bu cümlelerden zaten nereye varmak istediğimi anlatabilmişimdir. .Net in gelecek nesillerinin en büyük hedeflerinden birisi dinamik dillere ait nesneler ile konuşabilmek ve bunu mümkün olduğunca kolaylaştırmaktır. Bu noktada COM API' leri gibi nesnelerinde kullanımı söz konusudur. Aynen yukarıda geliştirdiğimiz örnekte olduğu gibi. Bu nedenle C# 4.0 içerisinde seçimlik parametre kullanımına izin veren geliştirmeler yapılmıştır(Optional Parameters) Buna göre yukarıdaki kod parçasını C# 4.0 stilinde aşağıdaki gibi geliştirebiliriz.

Optional Parameters ile

using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;

namespace NewFeatures2
{
    class Program
    {
        static void Main(string[] args)
        {
            Word.Application wrdApp = new Microsoft.Office.Interop.Word.Application();           
            wrdApp.Visible = true;           
            wrdApp.Documents.Open(@"C:\Yeni Ozellikler.docx");
            Console.WriteLine("Kapatmak için bir tuşa basınız");
            Console.ReadLine();
        }
    }
}

Bu kod parçası çalıştığında da aynı sonucu alırız. Yine Word belgesi açılacak ve içeriği görüntülenecektir. Hem kodun okunurluğu kolaylaşmıştır, hem de kısalmıştır. Diğer taraftan parametre değerini aktarırken ref kullanılmadığına dikkat etmemiz gerekiyor.(Ommit ref özelliği) Üstelik object tipinden değişken ataması yerine doğrudan dosya adresininin içeriğini gönderebildiğimizede dikkat edelim.

Tabi ihtiyaçlar bitmek bilmiyor. Burada görüldüğü gibi gereksiz olan parametrelerin hiç biri bildirilmemiştir. Ayrıca ref anahtar kelimeside herhangibir şekilde kullanılmamıştır. Ancak arada başka bir parametrenin daha kullanılması gerekirse... Undecided Söz gelimi 3ncü parametre dosyanın yanlız okunabilir(ReadOnly) modda açılıp açılmayacağını belirtir. Optional Parameter tekniğini kullanırsak ikinci parametreyi atlamamız mümkün olmayacaktır. Acaba böyle bir vakada kodu yine istemediğimiz şekliyle aşağıdaki gibi geliştirmemiz mi gerekir?

using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;

namespace NewFeatures2
{
    class Program
    {
        static void Main(string[] args)
        {
            Word.Application wrdApp = new Microsoft.Office.Interop.Word.Application();           
            wrdApp.Visible = true;           
            object fileNamePath = @"C:\Yeni Ozellikler.docx";
            object missingValue = Missing.Value;
            object onlyRead = true;

            wrdApp
                .Documents
                .Open(ref fileNamePath, ref missingValue, ref onlyRead, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue, ref missingValue);           

            Console.WriteLine("Kapatmak için bir tuşa basınız");
            Console.ReadLine();
        }
    }
}

Oysaki C# 4.0 bu gibi durumlar için isimlendirilmiş parametre(Named Parameters) kullanımını olanaklı kılmaktadır. Aşağıdaki şekilde görüldüğü gibi, intellisense' de bize yardımcı olmaktadır.

Dolayısıyla yukarıdaki kod parçasını aşağıdaki gibi geliştirebiliriz.

Named Parameters kullanımı ile

using System;
using System.Reflection;
using Word=Microsoft.Office.Interop.Word;

namespace NewFeatures2
{
    class Program
    {
        static void Main(string[] args)
        {
            Word.Application wrdApp = new Microsoft.Office.Interop.Word.Application();           
            wrdApp.Visible = true;
           
            wrdApp.Documents.Open(@"c:\Yeni Ozellikler.docx", ReadOnly: true);

            Console.WriteLine("Kapatmak için bir tuşa basınız");
            Console.ReadLine();
        }
    }
}

Son olarak Platform Interop Assembly(PIA) ile ilgili gelen yeniliklerden birisine değinmek istiyorum. Normal şartlarda Visual Studio 2010 öncesinde bir COM API' sini uygulamaya referans ettiğimizde, sarmalanan kütüphanenin özelliklerinde aşağıdaki şekilde görülen Embed Interop Types isimli bir kriter olmadığı bilinmektedir.

Oysaki Visual Studio 2010 ile birlikte bu özellikte gelmektedir. Bu tabiki sadece C# 4.0 diline bağlanacak bir yetenek olarak düşünülmemelidir.

Peki ne işe yarar? Eğer yukarıda geliştirdiğimiz C# 4.0 örneğinin ildasm(Intermediate Language DisAsseMbler) çıktısına bakacak olursak aşağıdaki durum ile karşılaşırız.

 

Göze çarpan özel bir nokta yer almamaktadır. Ancak Microsoft.Office.Interop.Word assembly' ının özelliklerinde yer alan Embed Interop Types seçeneğini true olarak değiştirir ve söz konusu uygulamanın IL çıktısına tekrardan bakarsak aşağıdak sonuçlarla karşılaşırız.

Görüldüğü gibi API içerisinde yer alan tipler, .Net programı içerisine birer tip olarak gömülmüştür. Aslında bu yenilik, PIA' ların, geliştirilen asıl uygulama içerisine tip bazında gömülerekten taşınabilmelerini kolaylaştırıcı bir özellik olarak görülebilir. Bu konudaki araştırmalarıma devam ediyorum. Yeni bilgiler kazandıkça sizlerle paylaşmaya devam ediyor olacağım.

Böylece geldik bir yazımızın daha sonuna. Bu yazımızda sizlere C# 4.0 ile birlikte gelen bir kaç yeniliği aktarmaya çalıştım. Sonuç olarak bu yeniliklerin özellikle dynamic tiplerin kullanımı kolaylaştırmak üzere getirildiğini söyleyebiliriz.

Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

Tags:  
Categories:   C# 4.0
Actions:   E-mail | del.icio.us | Permalink | Yorumlar (0) | Comment RSSRSS comment feed
Bookmark and Share