Yansıma (Reflection)

Eyl 08, 2013

Yansıma (Reflection), üst verinin (metadata) çalışma zamanında (runtime) incelenmesini sağlayan mekanizmadır. Yansıma ile metod adları, nesnenin veri tipi ve "Constructor" bilgileri alınabilmektedir.
.NET ‘te kullanılan herşeyin bir tipi vardır. Üyeleri öğrenilmek istenen bir tip, öncelikle bir "Type" değişkeni olarak alınmaktadır. Bu noktadan sonra Yansıma uzayına ait sınıfları ve metodlarını kullanarak ilgili tipe ait tüm bilgiler elde edilebilmektedir.
Tasarım yaparken en çok karşılaşılan problem uygulamanın esnek bir yapıya sahip olma sorunudur. Uygulamanın mümkün olduğu kadar modüler olmasını sağlamanın en önemli yollarından biri, eklentiler çözümüdür. Eklenti tabanlı programlar, bir çok yenilikle programcının işini kolaylaştırmaktadır. Bu konudaki en önemli özelliği "System.Reflection" isim alanıdır ve yansıma kullanılarak "Assembly" dosyaları hakkında bilgiler edinebilmeyi, metadata’larını sorgulayarak, onları tekrar tekrar hafızaya yükleyebilmeyi oldukça kolay bir hale getirir. "System.Reflection" isim alanı yansıma sırasında kullanılan sınıfları (class) temsil eder.
"Assembly" sınıfı "System.Reflection" içinde yer alır ve fiziksel bir "assembly" temsil edilir.

Assembly cls = Assembly.LoadFrom("test.dll");
Type[] types = cls.GetTypes();
DirectoryInfo d = new DirectoryInfo(Application.StartupPath);
FileInfo[] files = d.GetFiles("*.dll");
MethodInfo[] mthds = type.GetMethods();
ParameterInfo[] prms = info.GetParameters();

Yukarıdaki kod sayesinde, cls nesnesi test.dll dosyası ile temsil edilmektedir. Sırasıyla, test.dll dosyası içerisinde yer alan türler okunur, uygulamanın bulunduğu klasöre ait Directory nesnesi oluşturulur, uygulamanın çalıştığı klasördeki *.dll dosyaları FileInfo[] nesnesine atılmaktadır. "Type" cinsinden tip (type) oluşturularak "types" ın içindeki methotlar elde edilmektedir. "MethodInfo" türünden bilgi (info) oluşturularak methodun içindeki parametreler elde edilmektedir.
Yansıma ile 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ştirilebilmektedir.

"Type"dan nesne oluşturmak istenirse hemen "Activator.CreateInstance(typeof(...),params)" ile belirlenen bir yapıcı metod ile nesne oluşturulmaktadır. Başka bir yöntem ise;

class App
{
    private int x;
    public void fonk() 
    { 
        Console.WriteLine(x);
    }
    public App() 
    { 
        Console.WriteLine(“default”); 
    } 
    public App(int a) 
    { 
        x = a; 
    }
}

Başka bir örnek ise;

class Program
{
    private static void Main(string[] args) 
    { 
        Type type= typeof(App); 
        ConstructorInfo cin = type.GetConstructor(new Type[] {typeof(int)}); 
        App a = (App)cin.Invoke(new object[] {2}); 
        a.fonk(); 
    }
}

"Runtime" sırasında özellikle dinamik olarak yapılan işlem ve kontrollerde verim sağlayan bir özellik ise ("Constructor" ve metodlar getirilmiştir);

class Program
{
    static void Main(string[] args)
    {
        Type objectType = testObject.GetType();
        ConstructorInfo[] info = objectType.GetConstructors();
        MethodInfo[] methods = objectType.GetMethods();
        Console.WriteLine("Constructors:");
        foreach (ConstructorInfo cf in info)
            Console.WriteLine(cf);
        Console.WriteLine(); 
        Console.WriteLine("Methods:");
        foreach (MethodInfo mf in methods)
            Console.WriteLine(mf);
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Assembly a = Assembly.GetExecutingAssembly();
        Type t = a.GetType("ReflectionSample.Program");
        MethodInfo mi = t.GetMethod("Foo");
        foreach (ParameterInfo pi in mi.GetParameters())
            Console.WriteLine("{0} {1}", pi.ParameterType.Name, pi.Name);
    public void Foo(int a, long b)
    { 
    }
}


Uygulamalar

class Class1
{
    static void Main(string[] args)
    {
        Type tur=Type.GetType("System.Single");
        System.Reflection.MemberInfo[] turmembers=tur.GetMembers();
        Console.WriteLine(tur.Name.ToString()+" sinifindaki üye sayisi:"
                          +turmembers.Length.ToString());
        for (int i = 0; i < turmembers.Length;i++ )
            Console.WriteLine(i.ToString() + ". üye adi:"
                              + turmembers[i].Name.ToString()
                              + "||" + turmembers[i].MemberType.ToString());
    }
}

Öncelikle "String" sınıfının tipi öğrenilmektedir. Bu satırda, "System.Single" tipi içinde yer alan üyelerin listesini Yansıma uzayında yer alan, "MemberInfo" sınıfı tipinden bir diziye aktarılmaktadır. "Length" özelliği, "MemberInfo" tipindeki dizide yer alan üyelerin sayısını, (dolayısıyla "System. Single" sınıfı içinde yer alan üyelerin sayısını) vermektedir. İzleyen döngü ile, "MemberInfo" dizininde yer alan üyelerin birtakım bilgileri ekrana yazdırılır. "Name" özelliği üyenin adını verirken, "MemberType" özelliği ile üyenin tipi alınmaktadır. Bu üye tipi metod, özellik, yapıcı metod ve benzeridir.

class Class1
{
   static void Main(string[] args)
    {
        DataTable dt=new System.Data.DataTable();
        Type tipimiz=dt.GetType();
        MethodInfo[] turMetodlari=tipimiz.GetMethods();
        Console.WriteLine(tipimiz.Name.ToString()+" sinifindaki metod
                          sayisi:"+turMetodlari.Length.ToString());    
        for(int i=0;i < turMetodlari.Length ; i++)
        { 
            Console.WriteLine("Metod adi:"+turMetodlari[i].Name.ToString()+
                              " |Dönüs degeri:"
                              +turMetodlari[i].ReturnType.ToString()); 
            ParameterInfo[] paramInfo = turMetodlari[i].GetParameters();    
            Console.WriteLine("-----Parametre Bilgileri-----"
                              +paramInfo.Length.ToString()+
                              " parametre");
            for (int j = 0; j < paramInfo.Length;j++ )
                 Console.WriteLine("Param.Adi:"
                                   + paramInfo[j].Name.ToString()
                                   +"|Param.Tipi:"
                                   + paramInfo[j].ParameterType.ToString());
            Console.WriteLine("----");
       } 
   }
}

"DataTable" örneğinin "GetType" metodunu kullanarak, bu örneğin dolayısıyla "DataTable" sınıfının tipi elde edilmektedir. Sadece metodları incelemek istenildiğinden, "GetMethods()" metodunu kullanılmakta ve sonuçları "MethodInfo" sınıfı tipinden bir diziye aktarılmaktadır. Metod sayısı "Length" özelliği ile alınmakta, döngü oluşturulmakta ve metodları bir takım özellikleri ile birlikte yazdırmaktadır. Metodun ismi "Name" özelliği ile metodun dönüş tipi ise "ReturnType" özelliği ile alınmaktadır. Daha sonraki satırda ise, "i" indeksli metoda ait parametre bilgileri "GetParameters()" metodu ile alınmakta ve Yansıma uzayında bulunan "ParameterInfo" sınıfı tipinden bir diziye aktarılmaktadır. Böylece ilgili metodun parametrelerine ve parametre bilgilerine erişebilmektedir. Döngü ile "i" indeksli metoda ait parametrelerin isimleri ve tipleri yazdırılmaktadır. Bunun için "Name" ve "ParameterType" özellikleri kullanılmaktadır.