Makale Özeti

Reflection kısaca projeye daha önceden referans edilmemiş bir assembly’e çalışma zamanında ulaşabilmemizi sağlayan teknolojidir. Reflection sayesinde çalışma zamanında ulaştığımız bu assembly hakkında istediğimiz bilgileri alabiliriz. Bu bilgiler Metadata olarak adlandırılır...

Makale

Reflection kısaca projeye daha önceden referans edilmemiş bir assembly’e çalışma zamanında ulaşabilmemizi sağlayan teknolojidir.  Reflection sayesinde  çalışma zamanında ulaştığımız bu assembly hakkında istediğimiz bilgileri alabiliriz.   Bu bilgiler Metadata olarak adlandırılır.  Metadata, assembly’nin ismi, versiyonu, içindeki class’lar, class’ların özellikleri, olayları, metotları, metotların parametreleri gibi birçok şeyi kapsayabilir.  Metadata sayesinde assembly’deki nesneleri kullanabiliriz.  Uygulamanın önceden assembly’nin varlığı veya assembly’deki nesne, özellik, metot ve olaylar hakkında bilgi sahibi olması gerekmez, bütün bu bilgiler çalışma zamanında elde edilebilir.  Örneğin bir metodun parametre isteyip istemediği, istiyorsa kaç adet hangi tipte parametrenin gerektiği reflection kullanılarak öğrenilebilir ve yine reflection’la bu metot çalıştırılabilir.  Ayrıca, bu teknoloji kullanılarak çalışma zamanında hafızada bir assembly üretilebilir ve kullanılabilir.

 

.NET’te reflection’ı kullanabilmek için System.Reflection namespace’inden yararlanılır.  System.Reflection namespace’i yukarıda anlatılan işlemleri yapabilmek için gerekli nesneleri içerir.  Bu nesnelere geçmeden önce bazı kavramları tanıyalım.

 

Assembly

 

Assembly CLR tarafından çalıştırılan versiyonlanabilir ve yeniden kullanabilir uygulamaya verilen addır.  .NET dilleriyle yazdığınız uygulamalar birer assembly olarak tanımlanabilir.  Bu bir exe veya dll uzantılı dosya olabilir.  Assembly’nin en önemli özelliği kendisiyle ilgili versiyon, tip, içerik gibi bütün bilgileri yani metadata’sını üzerinde taşımasıdır.  Assembly’de bu bilgilerin bulunduğu bölüme Asembly Manifest denir.  Örneğin, DLL’ler eskiden kendi özellikleriyle ilgili bilgileri bilgisayarın registry’sine kaydettiğinden bir DLL’i kullanabilmek için önce onu sisteme tanıtmak gerekiyordu.  DLL sisteme tanıtıldığında gerekli bilgiler registry’e kaydediliyor ve DLL kullanılabilir hale geliyordu.  .NET’le birlikte artık bu işlem ortadan kalktı.  Yazdığımız uygulamanın sisteme tanıtılmasına gerek kalmamasının sebebi gerekli bilgilerin assembly’nin içinde uygulamayla birlikte taşınabilmesidir.  Yani yazdığınız uygulamayı başka bir bilgisayarda çalıştırmak için ekstra bir işlem yapmanıza gerek kalmaz, uygulamayı kopyaladığınızda gerekli bilgilerde assembly’le birlikte kopyalanmış olur.

 

.NET’te iki çeşit Assembly vardır:

 

  1. Private Assembly:  Private assembly sadece bir uygulama tarafından kullanılabilen, o uygulamaya ait assembly’dir.  Uygulamanın exe’siyle aynı klasörde bulunur.

 

  1. Shared Assembly:  Shared assembly ise bilgisayardaki bütün uygulamalar tarafından kullanılabilen assembly’lerdir.  Shared assembly’ler bilgisayarda Global Asembly Cache klasörü olan C:\WINDOWS\assembly’de bulunurlar.  Bir assembly’nin Shared olabilmesi için bir StrongName’i olmalı ve GAC’a eklenmelidir.

 

System.Type Class’ı

 

System.Type class’ı bir nesnenin türünü (class, interface, dizi, enumeration) tanımlar.  System.Reflection’ın fonksiyonelliğinin temelini oluşturur ve metadata’ya ulaşmanın en iyi yollarından biridir.  Bir class’ın olayları, metotları, özellikleri, oluşturucuları içinde bulunduğu modül ve assembly hakkında bilgi almak için Type class’ının üyelerinden yararlanılır.

 

LateBinding

 

Genel türde tanımlanmış bir değişkeni özel bir türe göre nesneleştirme olayına Late Binding denir. Bir nesneyi Object olarak tanımlayıp Button olarak kullanmak bu olaya bir örnek olarak düşünülebilir.

 

Dim Birsey As Object

Birsey = New Button Late Binding

Birsey.text = "Tıkla"

Birsey.location = New Point(50, 50)

Me.Controls.Add(Birsey)

 

LateBinding, değişkenleri tanımlamadan kullanmamıza izin verir.  Ancak kodun yavaş çalışmasına ve gerektiği gibi kullanılmazsa çalışma zamanında hatalara sebep olur.

 

LateBinding olayının gerçekleşebilmesinin temelinde reflection yatar.  .NET System.Reflection namespace’inden yararlanarak çalışma zamanında tür bilgisini alır ve nesneyi oluşturur.

 

LateBinding olayını kullanırken Option Strict özelliğinin Off olmasına dikkat edilmelidir.  Aksi halde kodlarda hata oluşacaktır.

 

System.Reflection Namespace’i

 

System.Reflection’un çalışma zamanında tür bilgisi almak ve bunlara dayanarak özellik, metot ve olay kullanmak için gerekli nesneleri barındırdığından daha önce bahsedilmişti.  Şimdi bu namespace’in içindeki bazı class’ları inceleyelim.

 

Class

Açıklama

Assembly

Assembly’yi yüklemek, bilgilerini almak ve bazı değişiklikler yapabilmek için kullanılır.

AssemblyName

Assembly’nin versiyon, culture gibi kimlik bilgilerini almak için kullanılır.

EventInfo

Belirtilen olayın bilgilerini getirir.

FieldInfo

Belirtilen field’ların bilgilerini getirir.

MemberInfo

EventInfo, FieldInfo, MethodInfo ve PropertyInfo için genel class’dır.

MethodInfo

Belirtilen metodun bilgilerini getirir.

Module

İçinde birden fazla dosya barındıran bir assembly’nin belirtilen metoduna ulaşmayı  sağlar.

ParameterInfo

Belirtilen parametrenin bilgilerini getirir.

PropertyInfo

Belirtilen özelliğin bilgilerini getirir.

 

System.Assembly Class’ı ve Dinamik Olarak (çalışma zamanında) Assembly Yüklemek 

 

System.Reflection namespace’inin altındaki Assembly class’ının metodları kullanılarak çalışma zamanında uygulamaya başka bir assembly yüklenebilir ve bu assembly’nin özellik, olay ve metotları kullanılabilir.  Assembly yüklemek için kullanılan metotlardan biri LoadFrom’dur.  Bu metot parametre olarak DLL’in yerini alır ve assembly’i yükler.

 

Örneğin reflectNesne isimli bir Class Library oluşturalım.  Bu reflectNesne’de Nesne isimli bir class bulunsun.  Aynı solution’a  reflectKullan isimli bir Windows uygulaması ekleyelim. 

 

 

 

reflectKullan uygulamasının içindeki forma System.Reflection namespace’ini aldıktan sonra bir buton ekleyelim ve butonun tıklanması olayına şu kodları yazalım:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

 

Uygulama çalıştırılıp butona tıklandığında reflectNesne DLL’i uygulamanın Application Domain’ine yüklenir.  Bu adımdan sonra DLL ile ilgili bilgileri alabiliriz.

 

 

Messagebox.Show ("Assembly Bilgileri: " & refAssembly.FullName & vbNewLine &

"Shared Assembly: " & refAssembly.GlobalAssemblyCache & vbNewLine &

"CLR versiyonu: " & refAssembly.ImageRuntimeVersion & vbNewLine & "Assemblynin yeri: " & refAssembly.Location & vbNewLine)

 

 

Assembly’deki class, modul veya interface gibi tür bilgileri [Assembly] class’ının çeşitli metotlatı kullanılarak alınabilir.  Tür bilgilerini alabilmek için daha önce bahsedilen System.Type class’ından yararlanılır.  Örneğin reflectNesne DLL’i içinde nesne ve reflectModule bulunsun, tür bilgilerini alabilmek için:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur() As Type = refAssembly.GetTypes

Dim ITur As IEnumerator = Tur.GetEnumerator

 

While ITur.MoveNext

Turler &= ITur.Current.name & vbNewLine

End While

 

MessageBox.Show(Turler)

 

 

 

Burda önce assembly LoadFrom metoduyla uygulamanın çalışma zamanında assembly’i yüklemesi sağlanıyor.  LoadFrom metodunun çalışması için gereken tek şey reflectNesne.dll dosyasının bilgisayardaki yeri.  Daha sonra yüklenen assembly’deki tür bilgileri GetTypes metoduyla alınıp, System.Type tipindeki Tur dizisine atanıyor.  Assembly’nin içinde birden fazla tür olabileceğinden Tur nesnesi dizi olarak oluşturuldu.  Daha sonra Tur nesnesinin içindeki türlerin isimleri Enumeration yöntemiyle alınıyor.

 

Unutulmaması gereken nokta, bütün bu işlemlerin yapılabilmesi için System.Reflection namespace’nin  forma eklenmiş olması gerektiğidir.

 

Reflection ile Nesne Instance’ı Oluşturmak

 

Reflection ile assembly hakkında bilgileri alabildiğimiz gibi assembly’deki bir class’ı nesne haline getirip yani instance’ını oluşturup, onun özellik, metot ve olaylarından yararlanabiliriz.  Bunun için kullanabileceğimiz iki yöntem var:

 

1.      Activator: Nesnenin instance’ını oluşturmak için System.Activator class’ından yaralanabiliriz.  Activator bize lokaldaki veya uzak makinedeki bir nesnenin instance’ını oluşturma veya uzak bilgisayarda varolan bir instance’ı referans etme imkanı tanır.  Bunun için Activator class’ının CreateComInstance, CreateInsatnce, CreateInstanceFrom ve GetObject gibi metotları kullanılabilir. Örneğin GetObject metodu uzak bir nesne veya XML Web Servisi oluşturmanızı sağlar.  Activator nesnesinden yaralanarak bir nesne instance’ı oluşturmak için kodlar şu şekilde oluşturulabilir:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refAssembly.GetType("reflectNesne.nesne")

Dim refNesne As Object = Activator.CreateInstance(Tur)

           

Bu kodlar reflectNesne DLL’i içindeki nesne class’ının bir instance’ını oluşturuyor.  Instance oluşturmak için önce assembly yükleniyor.  Daha sonra assembly içindeki nesne class’ının tür bilgisi Type tipindeki Tur değişkenine atanıyor.  Tur değişkeni, Activator class’ının CreateInstance metoduna parametre olarak gönderiliyor ve LateBinding yöntemiyle bir instance oluşturulmuş olunuyor.  Burada dikkat edilmesi gereken nokta GetType metoduna gönderdiğimiz nesne isminin büyük-küçük harf duyarlılığının bulunmasıdır.  Bu adımdan sonra refNesne’nin özellik metot ve olaylarını kullanabiliriz. 

 

2.      Invoke:    Bir instance oluşturulurken constructor  adı verilen nesneye başlangıç değerlerinin atanmasını sağlayan bir metot çağrılır.  Constructor metot (kurucu metot) bir nesnede bir veya daha fazla adet olabilir.  Nesnenin instance’ı oluşturulurken bu metoddan yararlanıldığına göre, constructor metodunu kullanarak çalışma zamanında bir instance oluşturulabilir.  System.Type class’ının Invoke metodu bu yöntemi kullanır.

 

reflectNesne uygulamasındaki nesne class’ına aşağıdaki constructor metodu ekleyelim:

 

Public Sub New()

      MsgBox("Instance oluşturuldu")

End Sub

 

Daha sonra reflectKullanici uygulamasındaki formda, butonun tıklanması olayına şunları yazalım:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refAssembly.GetType("reflectNesne.nesne")

Dim yapBil As ConstructorInfo = Tur.GetConstructor(New System.Type() {})

Dim refNesne As Object = yapBil.Invoke(New Object() {})

 

Burada Tur değişkenine nesne’nin türü atandıktan sonra  constructor’ını çağırmak için ConstructorInfo class’ından yararlanılıyor.  Tur’un GetConstructor metodu ile nesne’nin constructor bilgileri oluşturulan yapBil’e atanıyor.  Bu metot parametre olarak instance’ı oluşturulmaya çalışılan nesnenin constructor parametrelerinin tür bilgilerini ister, ancak kullandığımız constructor parametre almadığından sadece bir Type nesnesi gönderiyoruz.  Bundan sonraki adım artık constructor’ın çağırılarak instance’ın oluşturulduğu adımdır.  Object tipinde tanımlanan refNesne yapBil’in Invoke metoduyla assembly’deki nesne’nin instance’ına dönüştürülüyor.  Invoke metodu parametre olarak kullanılacak constructor metodun parametrelerinin değerlerini ister.  Ancak bizim kullandığımız metot parametre istemediğinden sadece bir object nesnesi gönderiryoruz.  Burada yine LateBinding olayı gerçekleşmiş durumda.  Type class’ının GetConstructor metodu ile ConstructorInfo’nun Invoke metotlarının istediği parametrelerin farkına dikkat edilmeli; birincisi sadece String, Integer gibi parametre türü istiyor, ikincisi ise bu parametrelerin değerini istiyor.

 

Bu kodlar çalıştırıldığında nesne’nin instace’ı oluşturlurken class’daki constructor metot çalıştırılacak ve ekrana "Instance oluşturuldu" uyarısı gelecektir. 

 

Aynı uyarı Activator class’ını kullandığımızda da görülür.  Çünkü nesne’nin instance’ı oluşturulurken constructor metot mutlaka çağırılacaktır. 

 

 

Reflection ile instance’ını oluşturduğumuz nesnenin herhangi bir üyesini çağırmak için Type class’ının InvokeMember metodundan faydalanılır. 

 

Reflection ile Nesneden Metot Çağırmak

 

Instance’ı oluşturulan nesnenin bir metodunu çağırmak için Type class’ının InvokeMember metodundan yararlanılabilir.

 

Örneğin nesne class’ında shared bir sub oluşturalım:

 

Public Shared Sub paylasilanMetot()

MsgBox("paylasilanMetot çalıştı")

End Su

 

frmKullanici formundaki buton tıklanma olayına şu kodları yazalım:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refAssembly.GetType("reflectNesne.nesne")

 

Tur.InvokeMember("paylasilanMetot", BindingFlags.InvokeMethod Or BindingFlags.Public Or BindingFlags.Static, Nothing, Nothing, Nothing)

 

 

InvokeMember metoduna gönderilen ilk parametre çağırılacak metodun adıdır.  Adı yazarken büyük-küçük harflerine dikkat etmek gerekir.  İkinci parametreyle BindingFlag’ler belirlenir.  BindingFlag’ler nesneden hangitipte üyenin çağırılacağını belirler.  Örneğin BindingFlags.InvokeMethod ile çağırılan üyenin bir metot olduğu, BindingFlags.Public ile bu metodun Public olduğu ve BindingFlags.Static ile de bu metodun Shared olduğu anlatılıyor.  Üçüncü parametre Binder parametresidir.  Herhangi bir veri dönüşümü yapmadığımızdan bu parametreye Nothing atandı.  Dördüncü parametre olan Target parametresine nesneden oluşturulan instance atanır, biz instance oluşturmadığımızdan bu parametre de nothing ile geçildi.  Son parametre ise args’dır.  Çağırılan metot herhangi bir parametre istemediğinden yine Nothing denildi. 

 

Paylaşılmayan bir metodu çağırmak için class’ın bir instance’ı oluşturulur ve BindingFlags.Instance olarak belirlenir.  Örneğin nesne class’ına şöyle bir sub ekleyelim:

 

Public Sub metot()

        MsgBox("Metot çalıştı")

End Sub

 

Bu metodu reflection kullanarak çağırmak için, frmKullanici’da kodları şu şekilde oluşturmak gerekir:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refNesne.GetType("reflectNesne.nesne")

Dim nesne As Object = Activator.CreateInstance(Tur)

Tur.InvokeMember("Metot", BindingFlags.Public Or BindingFlags.InvokeMethod Or BindingFlags.Instance, Nothing, nesne, Nothing)

 

 

 

Paylaşılan metoddan farklı olarak Dördüncü parametreye oluşturduğumuz instance’ı gönderiyoruz.  Böylece paylaşılmayan metot o instance üzerinden çalıştırılıyor.

 

Nesne class’ına Protected bir metot yazalım:

 

Protected Sub gizliMetot()

MsgBox("gizliMetot çalıştı")

End Sub

 

Bu metodu çağırmak için BindingFlags.NonPublic olarak ayarlanmalıdır:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refNesne.GetType("reflectNesne.nesne")

Dim nesne As Object = Activator.CreateInstance(Tur)

Tur.InvokeMember("gizliMetot", BindingFlags.NonPublic Or BindingFlags.InvokeMethod Or BindingFlags.Instance, Nothing, nesne, New Object() {})

 

 

Buraya kadar hep parametre istemeyen metotlar çalıştırdık.  Diyelimki çalıştıracağımız metot biri String ve biri Integer olmak üzere iki adet parametre istesin.  Nesne class’ına şu sub’ı ekleyelim:

 

Public Sub parametreliMetot(ByVal kelime As String, ByVal sayi As Integer)

MsgBox("Kelime " & kelime & vbNewLine & "Sayi " & sayi)

End Sub

 

Bu metodu reflection kullanarak çağırmak için, frmKullanici’da kodları şu şekilde oluşturmak gerekir:

 

Dim refAssembly As [Assembly] = [Assembly].LoadFrom("C:\reflectNesne.dll")

Dim Tur As Type = refNesne.GetType("reflectNesne.nesne")

Dim nesne As Object = Activator.CreateInstance(Tur)

Dim metotBil As MethodInfo = Tur.GetMethod("parametreliMetot", New Type() {GetType(String), GetType(Integer)})

Dim metot As Object = metotBil.Invoke(nesne, New Object() {CStr(txt1.Text), CInt(txt2.Text)})

 

 

 

Belirtilen metodu parametrelirini göndererek çağırabilmek için MethodIngo class’ından yaralanılıyor.  Bu class tipinden oluşturulan metotBil değişkenine Type class’ının GetMethod metodu kullanılarak istenen metodun bilgileri atanıyor.  GetMethod’a gönderilen ilk parametre çağırılacak metodun ismi, ikinci parametre ise çağırılacak metodun istediği parametrelerin türü.  Tür bilgisi dizi halinde gönderiliyor.  Burda parametre sırasına dikkat edilmelidir.  MetotBil nesnesinin Invoke metodu kullanılarak çalıştırılacak metodun hangi nesne üzerinden ve hangi parametre değerleriyle çalıştırılacağı belirtiliyor.  TextBox’dan gelen değer String türünden olduğu için 2. parametre gönderilmeden önce Integer’a dönüştürülmelidir.