C# 4.0 - Dynamic Olmak

Merhaba Arkadaşlar,

Uzun bir süredir (son bir senelik zaman dilimi içerisinde) C# 4.0 ile birlike gelen yeniliklerden haberdarız. Şöyle bir kaç sene öncesini hatırlıyorum da...Cool Visual Studio 2005, Whidbey kod adı ile yayınlanmış ve C# 2.0 ile birlikte gelen pek çok yenilik olmuştu. Ancak bunlar içerisinde belkide en önemli olanı, CLR(Common Language Runtime) çekirdiğinde değiştirilme yapılmasını da zorunlu kılan generic mimari kavramıydı. Tabiki generic dışında gelen, yield anahtar kelimesi, isimsiz metodlar(anonymous methods), static sınıflar ve diğerleride önemli gelişmelerdi. Zaman ilerledi ve C# 3.0 ile birlikte bu kez hayatımıza, generic modelinden daha fazla etki yapan LINQ(Language INtegrated Query) girdi. Bir geliştirici olarak her zaman için yeniliklere açık olmamız ve yakalayabildiğimiz ölçüde takip etmemiz gerektiğini düşünüyorum. Bu bir geliştirici için neredeyse bir yaşam tarzı. Dolayısıyla artık C# 4.0 üzerinde konuşmanın zamanı geldide geçiyor.

C# 4.0 ile birlikte gelen yeniliklerin daha çok dinamik çalışma zamanını(Dynamic Language Runtime-DLR) kullanan diller üzerinde odaklanmış durumda olduğunu söyleyebiliriz. Peki bu ne anlama geliyor? DLR tarafını ilgilendiren dillere ait nesneler ile daha kolay konuşulması olarak küçük bir sebep belirtebiliriz. Bu nedenle C# 4.0 ile birlikte gelen önemli yeniliklerden birisi olan dynamic anahtar kelimesi sayesinde, Python, Ruby veya Javascript ile üretilen nesnelerin C# 4.0 tarafında late-binding ile ele alınması mümkün. Hatta var olan .Net nesnelerinin reflection kullanılmadan ele alınması veya COM objelerine ait üyelerin çağırılmasında bu anahtar kelimeyi kullanabiliyoruz. Aslında C#' ın 2.0, 3.0 versiyonunda gelen yenilikler nasıl ki belirli ihtiyaçlar nedeni ile ortaya çıkmışsa, C# 4.0 ile gelen yenilikleride bu anlamda düşünmemiz ve araştırmamız gerekiyor.

Bu yazımda sizlerle dynamic kelimesi ile ilgili olan araştırmalarım sonucu elde ettiğim bilgileri paylaşıyor olacağım. İşe ilk olarak aşağıdaki şekilde görülen yapıya sahip olduğumuzu düşünerek başlayacağız.

Şimdi bu yapıyı kısaca açıklayalım. Commands isimli sınıf kütüphanesi(Class Library) IGraphic arayüzünü(Interface) uygulayan Circle ve Rectangle isimli sınıflara sahiptir.

IGraphic arayüzü

namespace Commands
{
    public interface IGraphic
    {
        void Draw();
    }
}

Circle sınıfı

using System;

namespace Commands
{
    public class Circle
        :IGraphic
    {
        #region IGraphic Members

        public void Draw()
        {
            Console.WriteLine("Circle...");
        }

        #endregion
    }
}

Rectangle Sınıfı

using System;

namespace Commands
{
    public class Rectangle
        :IGraphic
    {
        #region IGraphic Members

        public void Draw()
        {
            Console.WriteLine("Rectangle...");
        }

        #endregion
    }
}

Console Application tipinden olan uygulamamız, Commands isimli sınıf kütüphanesini referans etmekte olup başlangıçta aşağıdaki kod içeriğine sahiptir.

using Commands;

namespace CSharp4Features
{
    class Program
    {
        static void Draw<T>(T graphObject) 
            where T : IGraphic
        {
            graphObject.Draw();
        }
        static void Main(string[] args)
        {
            #region Başlangıçtaki durumumuz

            Draw<Circle>(new Circle());
            Draw<Rectangle>(new Rectangle());

            #endregion
        }
    }
}

Uygulamayı çalıştırdığımızda aşağıdaki sonucu alırız.

Bu kod parçasında dikkat edilmesi gereken önemli noktalardan birisi, generic Draw<T> metodudur. Burada yer alan generic T tipine IGraphic arayüzünden türeme koşulu getirilmiştir. Bu sebepten dolayı, IGraphic arayüzünü uygulayan tüm tiplere ait Draw metounu çağırmamızı sağlayan tek bir metod geliştirmiş oluyoruz. Dolayısıyla tek yapılması gereken, Draw<T> metodunun kullanıldığı yerde, doğru tipe ait(bu örnek için Rectangle veya Circle) nesne örneğini parametre olarak aktarmaktır.

Şimdi burada, bizi dynamic kelimesine götürecek bir veya bir kaç sebep arayacağız.  İşte bir kaç blog içerisinde yakaladığım ortak soru geliyor...Ya Console uygulaması, IGraphic arayüzüne erişemiyor olsaydı. Bunu ayarlamak son derece kolay. Tek yapmamız gereken IGraphic arayüzünün public olan erişim belirleyicisini kaldırmak.(Bir başka deyişle internal'a çekmek) Bu durumda Draw<T> metodumuz için derleme zamanı(Compile Time) hatası alınacaktır. Peki ne yapılabilir? Reflection tekniklerinden yararlanarak ilgili tipin Draw metodunun çağırılması sağlanabilir. Yani kodu aşağıdaki hale getirebiliriz.

using Commands;
using System.Reflection;

namespace CSharp4Features
{
    class Program
    {
        static void Draw<T>(T graphObject)
        {
            MethodInfo methodInfo = typeof(T).GetMethod("Draw");
            if (methodInfo == null)
            {
                System.Console.WriteLine("Method bulunamadı");
            }
            methodInfo.Invoke(graphObject, new object[0]);
        }

        static void Main(string[] args)
        {
            Draw<Circle>(new Circle());
            Draw<Rectangle>(new Rectangle());
        }
    }
}

İlk olarak typeof metodu ile T tipi elde edilmekte ve Draw isimli metod istenerek MethodInfo tipinden bir referansa aktarılmaktadır. Bilindiği üzere reflection mimarisinde, çalışma zamanında tipler ve üyelerine ait bilgiler elde edilmekte ve istenirse üyelerin yürütülmesi(örneğin metodların çağırılması) sağlanabilmektedir. Bu nedenle ilk olarak T tipinin çalışma zamanı referansı üzerinden Draw metodu elde edilmeye çalışılır. Sonrasında ise eğer MethodInfo referansı null değilse Invoke fonksiyonuna gerekli parametreler gönderilerek Draw metodunun icra edilmesi sağlanır. (Tabiki çalışma zamanında gelen T nesne örneğine ait olan Draw metodunun) Uygulamayı bu haliyle çalıştırdığımızda yine aynı sonuçları alırız.

Ancak tabiki metodun bu yeni halinde tip güvenliğinden(type-safety) bahsetmemiz mümkün değildir. T için herhangibir tip kullanılabilir.

Peki dynamic anahtar kelimesi burada nasıl bir yaklaşım sunmaktadır. İşte aynı metodun C# 4.0 için dynamic anahtar kelimesi ile yazılmış hali.

using Commands;
using System.Reflection;

namespace CSharp4Features
{
    class Program
    {     
        static void Draw<T>(T graphObject)
        {
            dynamic obj = graphObject;
            obj.Draw();
        }

        static void Main(string[] args)
        {
            Draw<Circle>(new Circle());
            Draw<Rectangle>(new Rectangle());
        }
    }
}

Bu seferde aynı çıktıyı alırız. Tabi burada dikkat edilmesi gereken bir kaç nokta vardır ve kodun kısalmış olması bunlardan birisi değildir Wink Öncelikli olarak Draw metodu, söz konusu Circle veya Rectangle nesne örneklerine çalışma zamanında bağlanmaktadır. Bu zaten bizim reflection tekniği ile yapmakta olduğumuz bir işlemdir. Diğer yandan .Net Reflector aracı yardımıyla üretilen uygulama koduna bakıldığında söz konusu metod için aşağıdaki IL çıktısının oluşturulduğunu görebiliriz.

private static void Draw<T>(T graphObject)
{
    object obj = graphObject;
    if (<Draw>o__SiteContainer0<T>.<>p__Site1 == null)
    {
        <Draw>o__SiteContainer0<T>.<>p__Site1 = CallSite<Action<CallSite, object>>.Create(new CSharpInvokeMemberBinder(CSharpCallFlags.None, "Draw", typeof(Program), null, new CSharpArgumentInfo[] { new CSharpArgumentInfo(CSharpArgumentInfoFlags.None, null) }));
    }
    <Draw>o__SiteContainer0<T>.<>p__Site1.Target(<Draw>o__SiteContainer0<T>.<>p__Site1, obj);
}

Görüldüğü gibi Draw<T> metodu içerisinde <Draw>o_SiteContainer0<T> isimli generic bir tipin kullanıldığını ve bununda IL(IntermediateLanguage) tarafına eklendiğini görmekteyiz. Bir başka deyişle derleme işleminden sonra yine reflection kullanılan kod parçaları içeriye dahil edilerek, Circle veya Rectangle tiplerinden olan nesnelerin Draw metodunun çağırılması sağlanmış oldu.

Burada dikkat edilmesi gereken önemli bir noktada şudur. Eğer Draw<T> metoduna, Circle ve Rectangle dışında bir tip atarsak(özellikle Draw metodu olmayan) bu durumda RuntimeBinderException tipinden bir istisna alırız.

Tabiki bu kısım ve detaylarını daha iyi kavramak için belki biraz daha zamana ihtiyacımız olacak. Ancak bu anahtar kelimenin tek kullanım şeklinin, reflection ile elde edilen tiplere ait üyelerin çağırılmasını kolaylaştırmak olmadığınıda belirtmek isterim. Öyleki, dynamic kelimesi ile COM objelerinin ve bu sayede unmanaged API' lerin dinamik olarak ele alınması mümkün olabilir. Hatta, JSON formatına sahip bir nesnenin dynamic kelimesi ile kolayca ele alınabileceğini söyleyebiliriz. Bu noktada Office API' sine ait nesnelerin dynamic kelimesi ile son derece etkili ve kolay kullanılabildiğini de belirtmek isterim. Üstelik işin içerisine yine C# 4.0 ile gelen opsiyonel ve isimlendirilmiş parametreler(Optional and Named Parameters) adlı yeniliklerinde girdiğini söyleyebilirim. Bunu bir sonraki blog yazımda ele almaya çalışacağım.

Özet olarak artık C# programlama dilinin, dinamik olarak türlendirilmiş tiplere ait nesnelerle daha kolay konuşabildiğini söyleyebiliriz.

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

NOT: Örnekler 2008 PDC'de yayımlanmış olan Visual Studio 2010 PreBeta sürümü üzerinden geliştirilmiştir.

Yorumlar (4) -

  • Hocam ellerinize sağlık. Uzun zamandır yazılarınızı okumaya fırsat bulamamıştım.
  • Rica ederim Bahadır Hocam. Beğenmene sevindim. Umarım arkasından gelen diğer yazılarda işine yarar.
  • Gerçekten güzel bir yazı olmuş.Verdiğiniz emek için teşekkürler hocam

Yorum ekle

Loading