Jump to content

C# - Reflection


wmismail

Recommended Posts

NET ile çok uzun zamandır çalışıyor olmasına karşın Reflection'ı hiç kullanmamış, hakkında pek fikir sahibi olmayan pek çok kişi tanıyorum. Bu makalede öncelikle Reflection'ın ne olduğu ve uygulama geliştirme sürecinin neresinde yer alabileceğine kısaca değinecek, sonrasında geliştirdiğimiz uygulamanın herhangi bir referans olmaksızın Assembly'leri runtime esnasında açarak içlerinde aradığı fonksiyonu bulup kullanmasını sağlayacağız.

Reflection ile metadatayı çalışma zamanında incelememizi sağlayan mekanizmadır diyebiliriz. Hepimizin bildiği IntelliSense, reflectionın en sık kullandığımız örneklerinden biridir. Örneğin Visual Studio içinde bir class� isminden sonra nokta tuşuna bastığımızda ilgili classa ait bütün metod, özellik ve olayları listeleyen bir menü açılır. Bu bilgiler elbette önceden tanımlanmış değildir ve yazdığımız kelimeye göre hazır bir listeden getirilmemektedir. Öyle olsa IntelliSense standart classlar ile sınırlı kalmak durumunda kalacaktı. Oysa kullandığımız IntelliSense noktadan önce yer alan class'a ait bütün metod, özellik ve olayların listesini siz noktaya bastığınız anda ilgili classa ait metadatadan okuyarak dinamik olarak oluşturuyor.

Base Class Library içinde System.Reflection namespacei Reflection işlemlerinde kullanacağımız classları içermektedir. Örneğin bu namespace içinde yer alan Assembly classı, fiziksel bir assembly'i temsil eder. Hemen örnekleyelim; Assembly asm = Assembly.LoadFrom("mshowto.dll");

Yukarıdaki satırda asm nesnesi, mshowto.dll dosyasını temsil etmeye başlamıştır. Bu noktada mshowto.dll dosyasının içinde yer alan türleri okumak için aşağıdaki kodu kullanırız:

Type[] types = asm.GetTypes();

Gördüğünüz gibi her ne kadar ilk bahsedildiğinde oldukça uzak gibi görünse de, loadLibrary ve getProcAddress ile kıyasladığımızda, .net platformunda kullanımı son derece basit bir yapı reflection.

Şimdi bahsettiğimiz örnek uygulamayı geliştirmeye başlayalım.

İşe boş bir solution oluşturarak başlıyorum.Oluşturduğum solution içinde 2 adet class library projesi oluşturuyorum. Bunları clsLibSample0 ve clsLibSample2 olarak adlandırıyorum.

1000001499_000.jpg

clsLibSample0 projesinde 2 adet public fonksiyon yer alacak. Bu fonksiyonlar çağrıldığı anki saati gösteren showTime() ve verilen 2 sayı ile, belirtilen operatöre göre işlem yaparak sonuç dönen calculate() fonksiyonu.

Bu fonksiyonların kodları şu şekilde olacak;

public stringshowTime()

{

return DateTime.Now.ToLongTimeString();

}

public stringcalculate(stringv0, stringv1, stringop)

{

try

{

decimal_v0 = Convert.ToDecimal(v0);

decimal_v1 = Convert.ToDecimal(v1);

switch(op)

{

case "+":

return(_v0 + _v1).ToString();

case "-":

return(_v0 - _v1).ToString();

case "*":

return(_v0 * _v1).ToString();

case "/":

return(_v0 / _v1).ToString();

default:

return "";

}

}

catch(Exceptionex)

{

returnex.Message;

}

}

clsLibSample1 projesinde yine 2 adet public fonksiyon yer alacak. Bu fonksiyonlar çağrıldığı anki tarihi gösteren showDate() ve verilen parametreye merhaba diyen SayHello() fonksiyonu.

Bu fonksiyonların kodları şu şekilde olacak;

public stringshowDate()

{

return DateTime.Today.ToLongDateString();

}

public stringsayHello(stringname)

{

return "Hello "+ name;

}

Şimdi işin eğlenceli kısmına geldi sıra. Solution'a bir windows application projesi ekliyor ve Form1'in tasarımını aşağıdaki gibi düzenliyorum.1000001499_001.jpg

İlk olarak form1 yüklendiği anda, form1 ile aynı klasörde yer alan ve uzantısı *.dll olan tüm assembly'leri alarak, içinde yer alan class'ları ve class'ların içindeki metodları listview kontrolünde görüntülüyorum.

ArrayListmthds = new ArrayList();

private voidForm1_Load(objectsender, EventArgse)

{

//listview'in ilk sütununun genişliğini ayarlıyoruz

listView1.Columns[0].Width = listView1.Width - 10;

//uygulamanın bulunduğu klasöre ait DirectoryInfo nesnesini oluşturuyoruz

DirectoryInfod = new DirectoryInfo(Application.StartupPath);

//uygulamanın çalıştığı klasördeki *.dll uzantılı dosyaları FileInfo[] nesnesine atıyoruz

FileInfo[] files = d.GetFiles("*.dll");

//bulduğumuz her dll dosyası için dönecek bir döngü oluşturuyoruz

foreach(FileInfofile infiles)

{

//sıradaki dll dosyasını asm nesnesine yüklüyoruz

Assemblyasm = Assembly.LoadFrom(file.FullName);

//dll dosyasının içinde yer alan type'ları listeliyoruz

Type[] types = asm.GetTypes();

//her bir type için dönecek bir döngü oluşturuyoruz,

//bu döngü içinde type'ın içinde yer alan metodları listeleyeceğiz

foreach(Typetype intypes)

{

//listview'a eklemek için bir listviewitem nesnesi oluşturuyoruz

//eklediğimiz bir metod değil type olduğu için ismini ve imagelist

//içinde class iconunun sırasını temsil eden 1 değerini veriyoruz

ListViewItemcItem = new ListViewItem(type.Name, 1);

//oluşturduğumuz listviewitem nesnesini listview'a ekliyoruz

listView1.Items.Add(cItem);

//metod isimlerini saklayan arraylist'e boş bir kayıt ekliyoruz

mthds.Add("");

//class'ın içinde yer alan metodları içerecek MethodInfo arrayini oluşturuyoruz

MethodInfo[] methods = type.GetMethods();

//ve class'In içinde yer alan metodları lsitelemeye başlıyoruz

foreach(MethodInfoinfo inmethods)

{

//gelen bir virtual metod ise listeye dahil etmiyoruz

if(!info.IsVirtual)

{

//metoda ait parametreleri içerecek ParameterInfo arrayini oluşturuyoruz

ParameterInfo[] prms = info.GetParameters();

if(prms.Length == 0)

{//parametresiz bir metodsa listview'a ekliyoruz

ListViewItemitem = new ListViewItem();

item.Text = " "+ type.Name + "->"+ info.Name;

item.ImageIndex = 0;

listView1.Items.Add(item);

mthds.Add(info.Name);

}

else if(prms.Length > 0)

{

//parametre içeren bir metodsa signature kısmını oluşturuyoruz

string_prms = " (";

foreach(ParameterInfoprm inprms)

{

_prms += prm.ParameterType.Name + " "+ prm.Name + ", ";

}

_prms = _prms.Remove(_prms.Length - 2, 2) + ")";

//ve metod ismi ile birlikte signature'ı da listview'a ekliyoruz

ListViewItemitem = new ListViewItem();

item.Text = " "+ type.Name + "->"+ info.Name + _prms;

item.ImageIndex = 0;

listView1.Items.Add(item);

mthds.Add(info.Name);

}

}

}

}

}

}

Bulunduğumuz noktada uygulamamızı çalıştırdığımızda karşımıza aşağıdaki ekran geliyor:

1000001499_002.jpg

Metodları listeledik. Şimdi seçtiğimiz metodu çalıştırmaya geldi sıra. Önce bir metodu listview'da seçtiğimizde ilgili textbox'a ismini getirelim:

if(listView1.SelectedItems.Count == 1)

{//listview'da seçilen bir Item varsa ArrayList içinde seçilen Item'ın indexine karşılık gelen metodun ismini textbox1'e yazdırıyoruz

textBox1.Text = mthds[(listView1.SelectedItems[0].Index)].ToString();

}

ve uygulamanın bulunduğu klasörde yer alan *.dll dosyaları içinde runtime sırasında aradığımız fonksiyonu bularak çalıştıralım ve dönen sonucu görüntüleyelim;

private

voidbutton1_Click(objectsender, EventArgse)

{

//uygulamanın çalıştığı klasöre ait DirectoryInfo nesnesini oluşturuyoruz

DirectoryInfod = new DirectoryInfo(Application.StartupPath);

//klasörde yer alan *.dll uzantılı dosyaların listesini alıyoruz

FileInfo[] files = d.GetFiles("*.dll");

//her bir *.dll dosyası için dönecek bir döngü oluşturuyoruz

foreach(FileInfofile infiles)

{

//sıradaki *.dll dosyasını temsil eden asm nesnesini oluşturuyoruz

Assemblyasm = Assembly.LoadFrom(file.FullName);

//assembly'nin içinde yer alan type ları alıyoruz

Type[] types = asm.GetTypes();

//her bir type için dönecek bir döngü oluşturuyoruz

foreach(Typetype intypes)

{

//type'ın içinde yer alan metodları listeliyoruz

MethodInfo[] methods = type.GetMethods();

//her bir metod için dönecek bir döngü oluşturuyoruz

foreach(MethodInfoinfo inmethods)

{

//eğer sıradaki metod, aradığımız metod ise

//(listview'da seçtiğimiz metod), bu metodu çalıştırıyoruz

if(info.Name == textBox1.Text)

{

//boş bir obje oluşturuyoruz

objectres = null;

//seçilen type için bir instance oluşturuyoruz

objectobj = Activator.CreateInstance(type);

//metodun parametlerini denetliyoruz

ParameterInfo[] prms = info.GetParameters();

if(prms.Length > 0)

{

//metoda gönderilecek parametreleri kullanıcının

//listbox2'ye eklemiş olması gerekiyor

//eğer metodun beklediği parametre sayısı ile listbox2'de yer alan

//parametre sayısı eşit değilse uyarı vererek duruyoruz

if(listBox2.Items.Count == 0)

{

label1.Text = "Bu metodu çalıştırmak için "+ prms.Length.ToString() + " parametre göndermeniz gerekiyor ancak siz "+ listBox2.Items.Count.ToString() + " parametre gönderdiniz.\r\nLütfen eksik parametreleri tamamlayarak yeniden deneyin.";

break;

}

//metodun beklediği parametre sayısına göre bir object array oluşturuyoruz

object[] oArr = new object[prms.Length];

//listBox2'de yer alan değerleri oluşturduğumuz array'e kopyalıyoruz

listBox2.Items.CopyTo(oArr, 0);

//metodu çalıştırarak (Invoke ederek) dönen değeri label1'e yazdırıyoruz.

label1.Text = obj.GetType().InvokeMember(info.Name, BindingFlags.Default | BindingFlags.InvokeMethod, null, obj, oArr).ToString();

}

else

{//eğer metod parametre beklemiyorsa metodu doğrudan çalıştırıyoruz

label1.Text = obj.GetType().InvokeMember(info.Name, BindingFlags.Default | BindingFlags.InvokeMethod, null,

obj, new object[] { }).ToString();

}}}}}

// parametre girilmişse parametre listesini temizliyoruz

listBox2.Items.Clear();

}

Uygulamamızın geldiği noktayı hemen test ediyoruz:

showTime() metodu:

1000001499_004.jpg

calculate() metodunun parametresiz kullanım denemesi:

1000001499_005.jpg

calculate() metodu. 19, 33 ve + parametreleri ile :

1000001499_006.jpg

sayHello() metodu:

1000001499_007.jpg

Gördüğünüz gibi herhangi bir referans olmadan, ayrı bir assembly içinde yer alan bir fonksiyonu çalıştırarak dönen değeri almak son derece kolay bir şekilde gerçekleştirilebiliyor.

Uygulamayı (ve makaleyi) burada sonlandırıyorum ancak bir sonraki bölümün konusu hakkında şimdiden fikir vermeyi ihmal etmiyorum.

Yazı dizisinin bir sonraki bölümünde MSIL yapısını ele alacak ve çalıştırdığımız fonksiyona ait MSIL kodunu runtime'da üreterek uygulamamızın aşağıdaki çıktıyı vermesini sağlıyor olacağız.

1000001499_008.jpg

Son bölümde ise MSIL, C# ve C++ dillerinin yapılarını daha detaylı inceleyecek ve uygulamamıza aşağıda görebileceğiniz son halini veriyor olacağız:

1000001499_009.jpg

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...