wmismail Posted August 10, 2008 Share Posted August 10, 2008 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. 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. İ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: 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: calculate() metodunun parametresiz kullanım denemesi: calculate() metodu. 19, 33 ve + parametreleri ile : sayHello() metodu: 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. 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: Link to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.