C Sharp Programlama Dili/Dinamik tipler
Programlama dilleri genel anlamda ikiye ayrılabilir. Bunlar dinamik diller ve statik dillerdir. C, C++, C# ve Java birer statik dildir. Python, Ruby ve JavaScript ise birer dinamik dillerdir. Dinamik diller daha fazla esneklik sağlar, çabucak uygulama geliştirilmesine imkan verir, ancak buna karşılık olarak daha yavaştırlar ve büyük çaplı projelerin geliştirilmesine uygun değildirler. Örneğin JavaScript'te aşağıdaki gibi kodlar yazılabilir:
var a;
a.Ozellik1="Ahmet";
a.Ozellik2=2;
a.Metot();
Burada a'nın tipini bilmiyoruz, ancak buna rağmen iki özelliğe ve bir metoda bu nesne üzerinden ulaşabiliyoruz. Halbuki statik dillerde böyle bir şey mümkün değildir. Statik dillerde a değişkeninin tipini açıkça belirtmeliyiz, daha sonra sadece o tipin tanımının içinde olan üye elemanlara o nesne üzerinden erişebiliriz. C#'ta dinamik dillerin bu özelliğini dynamic
anahtar sözcüğüyle taklit edebiliriz. C#'ta dinamik tiplerle çalışma CLR'in üzerine eklenen DLR katmanıyla olur. Bir dinamik nesne üzerinden herhangi bir üye eleman çağrıldığında C# derleyicisi ilgili tipin içinde ilgili üye elemanın bulunup bulunmadığı kontrolünü yapmaz, bu kontrolü çalışma zamanında DLR'ye bırakır. Örnek:
dynamic d=5;
d.Ozellik="Ahmet";
Main bloğunun içinde bu iki satırın olduğu program derlenebilir. Çünkü C# derleyicisi dinamik tiplerin üye eleman çağrımlarında herhangi bir kontrol yapmaz. Ancak derlenmiş bu programı çalıştıracak olursanız çalışma zamanı hatası oluşur. Çünkü DLR, int tipinden olan d değişkeniyle int tipinde olmayan bir özelliğe erişilmeye çalışıldığında ne yapacağını bilemez. Aslında DLR, CLR'den daha akıllıdır. Örnek:
dynamic d=Activator.CreateInstance(typeof(String),'C',5);
int uzunluk=d.Length;
Activator sınıfına ait olan CreateInstance() metodu aslında geriye object nesnesi döndürür. Ancak buna rağmen bu geriye dönen nesne üzerinden string sınıfının Length özelliğine erişebildik. Az önce dediğim gibi DLR, CLR'den daha akıllıdır, eğer ilgili üye eleman o değişkenin tipi içinde yoksa o değişkenin gizli tipine bakar, ilgili üye elemanı orada arar, bulursa çalıştırır.
Bir metot dinamik tipte parametre alabilir, geri dönüş tipi dinamik olabilir. Dinamik tipler ile statik tipler arasında rahatlıkla tip dönüşümü yapılabilir. Dönüşümün uyumluluğu derleme aşamasında değil, çalışma aşamasında yapılacaktır.
DynamicObject sınıfı ve IDynamicMetaObjectProvider arayüzü
değiştirAz önce int tipindeki dinamik bir nesne üzerinden Ozellik özelliğine erişmiştik. int tipinin içinde Ozellik özelliği olmamasına rağmen C# derleyicisi derlemeye izin vermişti, ancak çalışma zamanında hata oluşmuştu. Bu hatanın oluşmasının sebebi DLR'nin ilgili tip tanımının içinde olmayan bir üye çağrısı olduğu zaman ne yapacağını bilememesidir. Şimdi DLR'ye bu konuda yardımcı olacağız. Bir tipin dinamik olabilmesi için ilgili tipin ya IDynamicMetaObjectProvider arayüzünü kullanması gerekir, ya da DynamicObject sınıfından türemesi gerekir. DynamicObject abstract bir sınıftır ve IDynamicMetaObjectProvider arayüzünü uygulamıştır. IDynamicMetaObjectProvider içinde sadece aşağıdaki metot bulunur:
DynamicMetaObject GetMetaObject(Expression e)
Burada DynamicMetaObject, System.Dynamic isim alanındaki bir sınıftır. Expression ise System.Linq.Expressions isim alanında bulunan bir sınıftır. Biz burada dinamik tipler oluşturmak için IDynamicMetaObjectProvider arayüzü yerine, DynamicObject sınıfını kullanacağız. Örnek:
using System;
using System.Dynamic;
using System.Linq.Expressions;
using System.Collections.Generic;
class Sinif : DynamicObject
{
private Dictionary<string, object> ozellikler = new Dictionary<string, object>();
public int sayi;
public override bool TrySetMember(SetMemberBinder binder, object value)
{
ICollection < string > ics = ozellikler.Keys;
if (!ics.Contains(binder.Name))
ozellikler.Add(binder.Name, value);
else
ozellikler[binder.Name] = value;
return true;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
ICollection<string> ics = ozellikler.Keys;
if (!ics.Contains(binder.Name))
throw new Exception("Özellik bulunamadı.");
else
result = ozellikler[binder.Name];
return true;
}
public override DynamicMetaObject GetMetaObject(Expression e)
{
return base.GetMetaObject(e);
}
public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg, out object result)
{
if (binder.Operation == ExpressionType.Add)
result = sayi + ((Sinif)arg).sayi;
else
throw new Exception("Bu dinamik tip nesnesiyle sadece toplama işlemi yapabilirsiniz.");
return true;
}
public override bool TryConvert(ConvertBinder binder, out object result)
{
if (binder.Type == typeof(int))
result = sayi;
else
throw new Exception("Bu dinamik tipten sadece int tipine dönüşüm yapılabilir.");
return true;
}
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result)
{
result = sayi * (int)indexes[0];
return true;
}
public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value)
{
sayi = (int)indexes[0] * (int)value;
return true;
}
public override bool TryInvoke(InvokeBinder binder, object[] args, out object result)
{
result = args[0];
return true;
}
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
result = binder.Name + " metodunu çağırdınız. Verilen ilk parametre: " + args[0];
return true;
}
public override bool TryUnaryOperation(UnaryOperationBinder binder, out object result)
{
if (binder.Operation == ExpressionType.Increment)
result = sayi + 1;
else
throw new Exception("Bu tip tekli operatörlerden sadece ++ operatörünü destekler.");
return true;
}
}
class Program
{
static void Main()
{
dynamic d = new Sinif();
d.Ozellik = 5;
Console.WriteLine(d.Ozellik); //çıktı:5
dynamic d2 = new Sinif { sayi = 10 };
dynamic d3 = new Sinif { sayi = 15 };
Console.WriteLine(d2+d3); //çıktı:25
int i = (int)d2;
Console.WriteLine(i); //çıktı:10
Console.WriteLine(d2[2]); //çıktı:20
d2[2] = 3; //d2'nin sayi özelliğine 6 atandı
Console.WriteLine(d2.sayi); //çıktı:6
Console.WriteLine(d2(11)); //çıktı:11
Console.WriteLine(d2.Metot(2));
//çıktı: Metot metodunu çağırdınız. Verilen ilk parametre: 2
d2++;
Console.WriteLine(d2.GetType()); //artık d2 int oldu.
Console.WriteLine(d2);
}
}
Buradaki Sinif sınıfı normal üye elemanlar da içerebilir. Bir çakışma olması durumunda normal üye elemanlar çalışır. Örneğin sınıfta normal şekilde tanımlanmış ve tek bir int parametre alan Metot() isimli bir metot varsa ve programımızda örneğin d2.Metot(3);
çağrısı varsa bu durumda normal metot çalışır. Bu durum diğer bütün üye eleman tipleri için de geçerlidir.