Reflection Kullanımı

Husamettin Elalmis

Merhaba arkadaşlar, bu yazıda sizlere System.Reflection isim uzayı altında bulunan Reflection konusundan bahsedeceğim, bu konu hem uzun hem çok yere giden bir konu, dolayısıyla ana hatlarıyla bu makalede bahsediyor olacağım.

Reflection nedir?

  • Bir objenin içerisini okuma işine Reflection denir.

    • Objenin tipi

    • Objenin içerisinde tanımlanan Propertyler

    • Objenin içerisinde tanımlanan Methodlar

    • Objenin contructor tanımı

    • Tüm methodların parametreleri ve tipleri

  • Bir objenin içerisini okumak için önce o objenin tipini öğreniriz

    • typeof(obj) veya obj.GetType() komutları kullanılır.

Bilinmesi Gereken Kavramlar

  • Reflection kavramına geçmeden önce aşağıdaki kavramların biliniyor olması gerekmektedir. Bu kavramlar çerçevesinde Reflection kullanımı çok etkin ve başarılı sonuçlar vermektedir.

    • NameSpace (isim uzayı)

      • Framework seviyesinde gruplandırılmış isim alanları (Framework)

        • System

        • System.IO

        • System.Data

    • Assembly

      • Exe veya dll

        • .Net framework ile build edilmiş kütüphane veya uygulama

    • Object

      • Class

      • Interface

      • Enumeration

      • ...

    • GAC (Global Assembly Cache)

      • Sabit tanımlı NameSpace'ler windows işletim sisteminde GAC olarak isimlendirilerek ortak system klasöründe barındırılır

      • Projeye eklenen referanslar buradan okunabilir, heryerden erişimi vardır

      • Kendi yazdığımız bir dll kütüphanesini GAC'a tanımlayabiliriz, böylece tüm projelerde AddReference yaparken otomatik kullanıma açık olur.

    • Property (özellik)

      • Bir classın içerisinde yaşayan değişkenlere verilen isimdir. Getter, Setter özellikleri vardır.

    • Generic & Dinamik & Static

      • Generic: Tipi değişebilen demek

        • Generic Tip: Tipi belli olmayan, istenildiğinde tipi belli olan.

        • Generic Method: Tipi belli olmayan, her türlü tipi parametre olarak alabilen Method demek.

        • Generic Class: Tipi belli olmayan, içerisine o an belirlenen bir tip gönderilen demek.

        • Örnek: Listemde bir obje listesi olsun. Bu objelerimin tipi henüz belli değildir. Ben bu tipi setlediğimde tipi belli olsun. List à List<string>

          • T kullanımı, Type anlamına gelir. Generic kodlama yaparken sıkça kullanırız.

      • Dinamik: Barındırdığı eleman sayısı değişebilen demek

        • Dinamik Liste: Listeye her an yeni elemanlar katılabilir, ayrılabilir demek.

        • Örnek: Benim bir menu listem var, dinamik. Menüye istediğimiz zaman eleman ekleyebilir veya çıkartabiliriz.

      • Static: Sabit değer demek

        • Static değişken: Değeri uygulamanın başından sonuna kadar sabit olan, değişmeyen değer demek.

          • const string = "Test"

        • Static class: Classın instance'ı bir defaya mahsus oluşturulan ve yaşayan demek.

        • Static liste: İçerisindeki eleman sayısı sabit, hiç bir zaman değişmeyen, eklenmeyen, silinmeyen liste demek.

Nerelerde Kullanılır? Önemi nedir?

  • Çok amaçlı senaryolarda kullanılır

    • Generic yapılarda kullanılır, olmazsa olmazlardandır.

  • Mapping işlemlerinde kullanılır

    • Bir objeyi, bir diğer objeye map etme işlemini otomatik yaptırma amaçlı kullanılır.

  • Raporlama araçlarında kullanılır

    • Objenin tipi generic olan yapılarda, çok amaçlı raporlama araçlarında kullanılır. Reflection dışında herhangi bir muadili yoktur.

  • ADOQuerylerinde kullanılır

    • Yazılan SQL querylerinin obje listesine map edilmesi işlemlerinde kullanılır

      • Bu işi EntityFramework bizim yerimize otomatik olarak yapmaktadır. ADO.Net projelerinde mapping işlemini el ile yapmak meşakkatli ve zahmetlidir. Kod değişikliklerinde sıkça cebelleşilir. Bu yüzden Reflection yöntemleri ile otomatik map işlemlerinde kullanılır. (3th uygulamalar kullanılmaz ise, AutoMapper vb.)

  • Farklı veri formatlarının birbiriyle eşleştirme işlemlerinde kullanılır

    • Jsondan gelen bir datanın xml deki bir data ile eşleştirilmesi işlemlerinde kullanılabilir, vb.

    • Dbden gelen bir datanın bir obje ile eşleştirilmesi işlemlerinde kullanılabilir, vb.

  • Bir tipin, tüm methodlarının listelenmesinde parametrelerinin okunmasında kullanılır

    • Invoke komutu, generic tiplerde method imzasına uygun yapıda parametre gönderilmesini sağlar. Bu, generic yapılarda çok kullanışlı bir araç haline dönüşmektedir. Test Automation araçları bu temel ile yazılırlar.

  • Bir tipin, bir instance'inin new'lenerek oluşturulmasını sağlar

    • Activator.CreateInstance komutu, generic tiplerde, ilgili objenin bir kopyasını oluşturarak ram'e yükler. Çok amaçlı kullanımı vardır, bir çok senaryoyu çözümler.

  • Bir tipin, propertylerine otomatik olarak veri setlenmesini sağlar.

    • Bir tipin şablonu okutularak, istenilen sayıda obje türetilebilir ve başlangıç değerleri otomatik olarak setlenebilir. Generator araçları, AutomationTest araçları, DB araçları bu temel mantığa göre çalışır.

  • Bir exe veya dll dosyasının method ve parametre analizi yapılabilir (ReverseEngineering)

    • .NetFramework uygulamaları obsufcator(karıştırma) kullanılmamış ise tüm kod ve methodları görülebilir.

Örnek 01 – Objeye Reflection Uygulayalım

public
class
Ogrenci

{


public
string AdSoyad { get; set; }


public
int TCNo { get; set; }


public
bool IsMezun { get; set; }

 


public
int Hesapla(int a, int b)

{


return (a + b) / 2;

}

 


public
void TestMetod(object arg)

{


int var1 = 14;


string var2 = “test2”;

 


try

{


if (arg == null)

{


throw
new ArgumentNullException(“arg null olamaz”);

}


if (arg.GetType() == typeof(string))

{


throw
new ArgumentException(“arg string olamaz”);

}


// ..

}


catch (ArgumentException ex) when (ex.GetType().IsSubclassOf(typeof(ArgumentException)))

{

Console.WriteLine($”ArgumentException’un alt exceptionu oluştu {ex.GetType()}“);

}


catch (Exception ex)

{

Console.WriteLine($”Genel exception oluştu {ex.GetType()}“);

}


finally

{

var1 = 06;

var2 = “test3”;

}

}

}

 

 

private
static
void Test01()

{


var obj = new Ogrenci();

GetInfo<Ogrenci>(obj);

}

 

private
static
void GetInfo<T>(T obj)

{


var fullName = typeof(T).FullName;


var name = typeof(T).Name;

 

Console.WriteLine($”– obje bilgileri –“);

Console.WriteLine($”Tam Adı : {fullName}“);

Console.WriteLine($”Adı : {name}“);

Console.WriteLine();

 

Console.WriteLine($”– {name} objesinin propertyleri –“);

 


var props = typeof(T).GetProperties();


foreach (var item in props)

{

Console.WriteLine($”Property Adı: {item.Name} / Property Tipi: {item.PropertyType}“);

}

 


var methods = typeof(T).GetMethods();


if (methods.Any())

{

Console.WriteLine();

Console.WriteLine($”– {name} objesinin methodları –“);

 


foreach (var item in methods)

{

Console.WriteLine($”{item.Name} -> {item.MemberType} -> {item.ReturnType}“);

 

ParameterInfo[] parameters = item.GetParameters();


foreach (var item2 in parameters)

{

Console.WriteLine($”\tParametre -> {item2.Name} -> {item2.ParameterType}“);

}

 

MethodBody mb = item.GetMethodBody();

 


if (mb != null)

{


byte[] ilCode = mb.GetILAsByteArray();


int maxStackSize = mb.MaxStackSize;

IList<LocalVariableInfo> localVariables = mb.LocalVariables;

Console.WriteLine($”\tMaxStackSize: {maxStackSize}“);

 


if (localVariables.Any())

{

Console.WriteLine(“Local variables:”);


foreach (LocalVariableInfo item2 in localVariables)

{

Console.WriteLine($”\tLocalType:{item2.LocalType} LocalIndex:{item2.LocalIndex}“);

}

}

 

Console.WriteLine(“IL kodu:”);

StringBuilder stringifiedIlCode = new StringBuilder();


foreach (byte b in ilCode)

{

stringifiedIlCode.Append(string.Format(“{0:x2} “, b));

}

 

Console.WriteLine($”\t{stringifiedIlCode}“);

}

}

}

}
  • Objenin ne olduğu önemsizdir

    • Reflection komutları ile objenin içini tarayabiliyoruz

  • Nerde işime yarar?

    • Ogrenci classını bir methoda gönderip, property değerlerine göre ve value değerlerine göre Rapor hazırlamak istersem işime yarar.

    • Ogrenci classı olmak zorunda değil, herhangi bir class da olabilir. Senaryoya göre class gönderebiliriz.

    • Generator yazmak istersem işime yarar, şekilden şekile sokabiliriz.

Örnek 02 – Dümdüz Activator.CreateInstance Kullanalım

var t = typeof(Ogrenci);


var obj = Activator.CreateInstance(t);

 


if (obj is Ogrenci ogrenci)

{

ogrenci.AdSoyad = “Hüsamettin ELALMIŞ”;

Console.WriteLine(ogrenci.AdSoyad);

}var t = typeof(Ogrenci);


var obj = Activator.CreateInstance(t);

 


if (obj is Ogrenci ogrenci)

{

ogrenci.AdSoyad = “Hüsamettin ELALMIŞ”;

Console.WriteLine(ogrenci.AdSoyad);

}
  • Ogrenci classından 1 adet instance oluşturarak AdSoyad'ına setleme işlemi yaptık.

    • Burada anahtar kelime Activator.CreateInstance(t)'dir.

      • Bu işlemi tabi ki Ogrenci obj = new Ogrenci(); diyerek yapabilirdik ama bu şekilde yazarsak generic olmaz, static olur. Bir sonraki örnekte bunu generic yapıya dönüştüreceğiz, o zaman kullanımı daha net ortaya çıkacak.

Örnek 03 – Generic Activator.CreateInstance Kullanalım

private
static T Test05<T>(string fieldName, string deger)

{


var t = typeof(T);


var obj = Activator.CreateInstance(t);

 


var oprop = t.GetProperties().FirstOrDefault(x => x.Name.ToUpper() == fieldName.ToUpper());

oprop.SetValue(obj, deger);

 


return (T)obj;

}

 


var ogrenci = Test05<Ogrenci>(“AdSoyad”, “Hüsamettin ELALMIŞ”);

Console.WriteLine(ogrenci.AdSoyad);
  • Test05 methodu, generic bir method oldu.

    • Bu methoda hangi objeyi, hangi propertyi gönderirsek ona setleme işlemi yapacak.

      • Generic tipleri kullanmanın en büyük avantajı budur, tip her zaman değişebilir!

Örnek 04 – String tipinin methodları nelerdir? Görelim

private static void Test06()

{

Console.WriteLine($”== method listesi ==”);

List<string> list = typeof(string).GetMethods().Select(x => x.Name).Distinct().OrderBy(x => x).ToList();

foreach (var item in list)

{

Console.WriteLine(item);

}

}

  • String bir tipe uygulanabilecek methodları framework seviyesinde görebiliyoruz

    • Buradan hareketle, tüm NameSpace'ler altındaki tüm methodları listeleyebiliriz

      • Bu methodlar, XML dokumantasyonları ile entegre olduğu için methodların ne işe yaradıkları ve parametre listelerini de alt alta yazdırabiliriz. Tüm framework hizmetimizde.

Örnek 05 – Generic tipe göre methodları görelim?

private
static
void Test07<T>()

{

Console.WriteLine($”== {typeof(T).Name} tipinin method listesi ==”);

List<string> list = typeof(T).GetMethods().Select(x => x.Name).Distinct().OrderBy(x => x).ToList();


foreach (var item in list)

{

Console.WriteLine(item);

}

}

 

Test07<string>();

Test07<int>();

Test07<float>();

  • İstediğimiz tipin methodlarını görebiliriz

Örnek 06 – SessionLimit.exe Assembly Çağıralım?

private
static
void Test03()

{


string filePath = @”C:\Program Files (x86)\Arksoft A.S\SessionLimit\SessionLimit\SessionLimit.exe”;

AssemblyName an = AssemblyName.GetAssemblyName(filePath);

Assembly asm = Assembly.Load(an);


var types = asm.GetTypes().Where(x => x.IsClass || x.IsEnum || x.IsInterface).ToList();


var types2 = asm.GetExportedTypes();

 


foreach (var item in types)

{

Console.WriteLine($”{item.Namespace} -> {item.Name}“);


var constants = item.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy).Where(fi => fi.IsLiteral && !fi.IsInitOnly).OrderBy(x => x.Name).ToList();

 


for (int i = 0; i < constants.Count; i++)

{

Console.WriteLine($”\t** constant {i + 1} ** -> {constants[i]}“);

}

 


var methods = item.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.InvokeMethod).OrderBy(x => x.Name).ToArray();


foreach (var item2 in methods)

{

Console.WriteLine($”\t{item2}“);

}

 

Console.ReadLine();

Console.Clear();

}

 

}

  • Assembly içerisindeki tüm methodları ve parametreleri görebiliriz

Örnek 07 – SessionLimit.exe Derinliklere İnelim

  • Exe veya dll dosyalarının içerisinde bulunan static stringler görülebilmektedir.

    • Buradan hareketle, URL adresini kurcalayan birisi sunucu URL üzerine PostAttack yapmayı deneyebilir.

    • Bunlar, genellikle penetrasyon testlerinde ortaya çıkarılır

Örnek 08 – Sabit Değerler Yakalanır

  • Sabit değerler genellikle yakalanır

    • Bunlar genellikle sızma testlerinde ortaya çıkarılır

    • Dll'lerin içeriği okunabildiği için sabit değerlerden kaçınmakta fayda vardır

    • Önemli bilgi ifşaları sisteme sızılmasına yol açabilir

Ekler

  • Bu makale ile birlikte Reflection kullanılarak oluşturulmuş 2 adet text dosyası eklenmiştir

    • assemblyList.txt (tüm namespace altındaki tüm methodların isimleri ve dönüş tipleri listelenmiştir, referans amaçlı kullanılabilir)

    • efMethodList.txt (tüm ef methodlarına ilişkin XML documanı eşleştirilerek oluşturulmuştur, referans amaçlı kullanılabilir)

  • assemblyList.txt

  • efMethodList.txt

Sonuç

  • Reflection, genellikle backend tarafında framework hazırlama işlemlerinde ağırlıklı olarak kullanılmaktadır

  • Generic yapılarla birlikte kullanımı çok etkilidir

Saygılarımla,

Hüsamettin ELALMIŞ – 11.07.2021

[email protected]

Last updated