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 ile Parametre Bilgisi Almak

 

Bir önceki örnekte bahsedilen parametreli metodu çağırmak için önceden parametre bilgisine ihtiyacımız vardır.   Parametre bilgisini reflection ile çalışma zamanında alabilmek için ParameterInfo class’ından yararlanılır.

 

Örneğin nesne class’ında şu fonksiyon olsun:

 

Public Function Siparis(ByVal UrunID As String, Optional ByVal Adet As Integer = 100, Optional ByVal Nakit As Boolean = True) As String

If Nakit Then

Return (Adet & " tane " & UrunID & " nolu ürün sipariş edildi. Ödeme nakit olacak")

Else

Return (Adet & " tane " & UrunID & " nolu ürün sipariş edildi.  Ödeme taksitli olacak")

End If

End Function

 

Bu fonksiyonu parametreleriyle ilgili bilgi almak için frmKullanici formunda şu kodları yazmak yeterlidir.

 

Dim s As New System.Text.StringBuilder

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

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

Dim metot As MethodInfo = Tur.GetMethod("Siparis")

Dim params() As ParameterInfo = metot.GetParameters

Dim param As ParameterInfo

For Each param In params

s.Append("Isim = ")

s.Append(param.Name)

s.Append(", Tip= ")

s.Append(param.ParameterType.ToString)

s.Append(", Seçimlik= ")

s.Append(param.IsOptional)

s.Append(", Yer= ")

s.Append(param.Position.ToString)

s.Append(", Varsayilan Değer = ")

s.Append(param.DefaultValue)

s.Append(vbNewLine)

Next

MessageBox.Show(s.ToString)

 

 

Parametre’leri almak için önce Siparis isimli fonksiyonun bilgileri MethodInfo tipindeki metot nesnesine aktarılıyor. Bu nesnenin GetParameters metodunu kullanılarak Siparis fonksiyonunun aldığı parametreler ParameterInfo tipindeki diziye gönderiliyor.  Daha sonra bu dizinin içideki ger parametrenin isim, tip, pozisyon ve varsayılan değer bilgileri bir StringBuilder nesnesinden yaralanılarak görüntüleniyor.

 

Reflection ile Özellik Bilgisi Almak

 

Reflection ile class’ta yeralan özellikler hakkında bilgi alınabilir ve bu özelliğe değer atanıp değer okunabilir.  Önce class’taki özellik bilgilerinin nasıl elde edileceğini inceleyelim:

 

Nesne class’ına aşağıdaki özelliği ekleyelim:

 

Private mOzel As String

Public Property Ozel() As String

Get

Return mOzel

End Get    

Set(ByVal Value As String)

mOzel = Value

End Set

End Property

 

frmKullanici formunda özellik bilgilerini alalım:

 

Dim s As New System.Text.StringBuilder

Dim Ozellikler() As PropertyInfo = Tur.GetProperties

Dim Ozellik As PropertyInfo

 

For Each Ozellik In Ozellikler

s.Append("İsim: " & Ozellik.Name & vbNewLine & _

"Okunabilir mi: " & Ozellik.CanRead & vbNewLine & _

"Yazılabilir mi: " & Ozellik.CanWrite & vbNewLine & _     

"Özelliğin veri türü: " & Ozellik.PropertyType.ToString & vbNewLine & _

"Özelliğin ait olduğu nesne: " & Ozellik.ReflectedType.ToString & vbNewLine & _

"Özelliğin attributeları: " & Ozellik.Attributes.ToString)

Next

 

MessageBox.Show (s.ToString)

 

 

Aynı özelliğe değer atamak ve atanan değeri okuyabilmek için ortak bir instance üzerinde çalışmak gerekir.  Bu sebeple assembly, type ve object nesnelerini Global olarak tanımlanmalı.

 

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

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

Dim nesne As Object = Activator.CreateInstance(Tur)

 

Private Sub btnAta_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAta.Click

 

Tur.InvokeMember("Ozel", BindingFlags.SetProperty Or BindingFlags.Public Or BindingFlags.Instance, Nothing, nesne, New Object() {CStr(txtDeger.Text)})

 

End Sub

 

Private Sub btnOku_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles BbtnOku.Click

 

Dim deger As Object = Tur.InvokeMember("Ozel", BindingFlags.GetProperty Or BindingFlags.Instance Or BindingFlags.Public, Nothing, nesne, New Object() {})

 

MessageBox.Show (deger.ToString)

 

End Sub

 

btnAta butonuna tıklandığında txtDeger TextBox’ındaki değer özelliğe gönderiliyor.  Bu atam işlemi için Tur nesnesinin daha önce metot çağırmak için de kullandığımız InvokeMember metodundan yararlanılıyor.  Özelliğe atama yapacağımızdan BindingFlag’lerden biri SetProperty olarak ayarlanıyor.

 

btnOku butonundaki kodlarda dönen değeri alabilmek için InvokeMember metodundan dönen değer Object tipinden bir değişkene atanıyor.  Özelliğin değerinin alınacağını anlatmak için BindingFlags.GetProperty olarak ayarlanıyor.

 

Reflection ile Field Bilgisi Almak

 

Field class’da

 

Public genelDeger As String

Private ozelDeger As Integer

 

şeklinde değişken tanımlamalarının yapıldığı yerdir.  Reflection aracılığıyla Özellik gibi bu değişkenlere de değer atanabilir ve değer okunabilir.  Atadığımız değeri okuyabilmek için yine aynı instance üzerinde çalışmalıyız.

 

nesneClass’ına yukarıdaki tanımlamaları ekledikten sonra frmKullanici’da kodları şu şekilde oluşturalım:

 

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

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

Dim nesne As Object = Activator.CreateInstance(Tur)

 

Private Sub btnAta_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAta.Click

 

Tur.InvokeMember("genelDeger", BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.SetField, Nothing, nesne, New Object() {TextBox1.Text})

 

End Sub

 

Private Sub btnOku_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOku.Click

 

Dim oku As Object = Tur.InvokeMember("genelDeger", BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.GetField, Nothing, nesne, Nothing)

 

MessageBox.Show(oku.ToString)

 

End Sub

 

genelDeger isimli değişkene değerini atamak için InvokeMember metodunda BindingFlags.SetField olarak ayarlanıyor.  Ayrıca değişken public olduğundan BindingFlags.Public de tanımlamalara ekleniyor

 

 

Private bir değişken üzerinde çalışabilmek için BindingFlags.NonPublic olarak belirlenmelidir.

 

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

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

Dim nesne As Object = Activator.CreateInstance(Tur)

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

 

Tur.InvokeMember("ozelDeger", BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.SetField, Nothing, nesne, New Object() {CInt(TextBox1.Text)})

 

End Sub

 

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click

 

Dim oku As Object = Tur.InvokeMember("ozelDeger", BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.GetField, Nothing, nesne, Nothing)

 

MessageBox.Show(oku.ToString)

 

End Sub

 

Burada BindingFlags’ın NonPublic olmasının yanında TextBox’dan gelen değer göndermeden önce Integer’a çevirilmek durumundadır.  Bunun yerine bir Binder’da atanabilirdi.  Binder class ilerleyen bölümler de açıklanacaktır.

 

Reflection ile Nesnenin Varsayılan Üyesini Çağırmak

 

Bir class’daki herhangi bir üye varsayılan üye olarak tanımlanabilir.  Bu durumda nesne’den varsayilan üye çağırıldığında  class’ın bu üyesi kullanılacaktır.  Bir class’da varsayilan üye tanımlamak için DefaultMember attribute’u kullanılır.  Örneğin nesne class’ı içine şu sub’ı yerleştirelim:

 

Public Shared Sub VarsayilanMetot()

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

End Sub

 

Bu metodu varsayilan üye yapmak için class tanımlamasını şu şekilde değiştirelim:

 

<DefaultMember("varsayilanMetot")> _

Public Class nesne

 

            Kodlar

            ...

Public Shared Sub varsayilanMetot()

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

End Sub

 

            Kodlar

            ...

                            

End Class  

 

frmKullanici formundaki butonun tıklanması 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("", BindingFlags.Static Or BindingFlags.Public Or BindingFlags.InvokeMethod, Nothing, Nothing, Nothing)

 

 

 

Reflection ile metot çağırmak için kullanılan kodlarda yanlızca metot ismi parametresi bş  gönderiliyor.  Bu durumda class’ta varsayılan olarak işaretlnen varsayilanMetot çalıştırılıyor.

 

Reflection ile Attribute Bilgisi Almak

 

Yazılan kodlara farklı özellikler kazandırmak veya assembly’deki metada’da yer alacak açıklamalar yazabilmek için attribute’lar kullanılır.  Attribute bilgisi reflection yöntemiyle okunabilir.  Örneğin nesne class’ındaki attribute tipini almak için frmKullanici’daki butona şu kodlar yazılabilir:

 

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

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

Dim atts() As Object = Tur.GetCustomAttributes(False)

Dim att As Attribute

For Each att In atts

MsgBox(att.GetType.ToString)

Next

 

 

Binder Class’ı Nedir, Nasıl Oluşturulur?

 

Binder’lar reflection yapılırken değerlerin tür dönüşümlerini yapmak veya ASP.NET’te olduğu gibi kontrollere veri bağlamak için kullanılır.  System.Reflection.Binder class’ı MustInherit olarak tanımlanmıştır, yani inherit edilmeden kullanılamaz.  Binder class’ına kendi istediğimiz dönüşümleri yaptırmak için bazı metotlarını override edebiliriz. 

 

Örneğin baglayici adında basit bir Binder class tanımlamak için önce reflectNesne projesine bu isimde bir class ekleyelim.  Sonra bu class’a Binder’ı inherit edelim ve ChangeType metoduna şu kodları yazalım:

 

Try

If value.GetType Is GetType(String) Then

Return CInt(value)

Else

            Return value

End If

Catch ex As Exception

      Return value

End Try

 

Bu kodlar String olarak gelen bir değeri Integer’a dönüştürüyor.  Sonuç olarak class’taki kodlar şu şekilde oluşmuş olur:

 

Public Class baglayici

    Inherits System.Reflection.Binder

 

Public Overrides Function BindToField(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.FieldInfo, ByVal value As Object, ByVal culture As System.Globalization.CultureInfo) As System.Reflection.FieldInfo

End Function

 

Public Overrides Function BindToMethod(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.MethodBase, ByRef args() As Object, ByVal modifiers() As System.Reflection.ParameterModifier, ByVal culture As System.Globalization.CultureInfo, ByVal names() As String, ByRef state As Object) As System.Reflection.MethodBase

End Function

 

Public Overrides Function ChangeType(ByVal value As Object, ByVal type As System.Type, ByVal culture As System.Globalization.CultureInfo) As Object

        Try

            If value.GetType Is GetType(String) Then

                Return CInt(value)

            Else

                Return value

            End If

        Catch ex As Exception

            Return value

        End Try

End Function

 

Public Overrides Sub ReorderArgumentArray(ByRef args() As Object, ByVal state As Object)

End Sub

 

Public Overrides Function SelectMethod(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.MethodBase, ByVal types() As System.Type, ByVal modifiers() As System.Reflection.ParameterModifier) As System.Reflection.MethodBase

End Function

 

Public Overrides Function SelectProperty(ByVal bindingAttr As System.Reflection.BindingFlags, ByVal match() As System.Reflection.PropertyInfo, ByVal returnType As System.Type, ByVal indexes() As System.Type, ByVal modifiers() As System.Reflection.ParameterModifier) As System.Reflection.PropertyInfo

End Function

 

End Class

 

Örneğin Public field’a değer atama konusunda nesnede Integer tipte belirlenmiş bir değişkene reflection yöntemiyle TextBox’dan değer gönderirken CInt fonksiyonunu kullanmak zorunda kalmıştık.  Dönüştürme işlemini gerçekleştirmek için yukarıda tanımlanan baglayici Binder’class’ından da yaralanabiliriz.

 

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

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

Dim nesne As Object = Activator.CreateInstance(Tur)

Dim bagla As New baglayici

Tur.InvokeMember("ozelDeger", BindingFlags.Instance Or BindingFlags.NonPublic Or BindingFlags.SetField, bagla, nesne, New Object() {TextBox1.Text})

 

Önceki kodlardan farklı olarak baglayici class’ından bir nesne oluşturluyor ve InvokeMember metodunun binder parametresi olarak atanıyor.  TextBox’dan gelen değeri dönüştürmek için bu Binder kullanılacağından CInt fonksiyonu kullanılmıyor.

 

Reflection ile olay tetiklemek

 

Reflection yöntemleriyle bir class’taki olay tetiklenerek formda bir işin yapılmasını sağlayabiliriz.  Örneğin nesne class’ına şu olayı ve tetikleyicisini ekleyelim:

 

Public Event olay As EventHandler

Public Sub tetikle()

RaiseEvent olay(Me, EventArgs.Empty)

End Sub

 

Nesne class’ında bu olay tetiklendiğinde frmKullanici’da çalışacak sub şu olsun:

 

Public Sub calis(ByVal sender As Object, ByVal e As EventArgs)

MessageBox("Olay yakalayici calisti")

End Sub

 

frmKullanici’daki buton yardımıyla reflection yöntemini kullanarak class’daki olay’ı tetikleyebilmek için kodlar şu şekilde oluşturulmalıdır:

 

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

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

Dim nesne As Object = Activator.CreateInstance(Tur)

Dim refOlay As EventInfo = Tur.GetEvent("olay", BindingFlags.Public Or BindingFlags.Instance)

refOlay.AddEventHandler(nesne, New EventHandler(AddressOf Me.calis))

nesne.tetikle()

 

Reflection ve Güvenlik

 

Buraya kadar, reflection’ın bir assembly’nin üyeleri hakkında bilgi edinmeyi ve onları kullanmayı sağladığını gördük.  Bu durumda örneğin internetten indirilen herhangi bir assembly’i referans etmeden istediğimiz gibi kullanabiliriz. Bu derece kontrolsüz bir kullanım hatalı sonuçlar oluşturabilir.  Burada reflection kodları için bazı kısıtlamalar yapılabilir.  Eğer reflection için yazılan kodlar Reflection.Permission iznine sahipse assembly’deki bütün türlere ulaşabilirler.  Bu izne sahip değilse reflection’la  sadece Public türlere ulaşılabilir.  Reflection.Permission izni olmadan Public olmayan türlere reflection yöntemleriyle erişilemez.