18 Ocak 2018 Perşembe

C# Attributes (Nitelikler) - Yazı Serisi - 3


Merhaba,

Daha önceki yazı serilerimizde Runtime Type Identification ve Reflection'dan bahsetmiştik. Bu yazıda da aslında Reflection'un bir parçası olan "Nitelikler"i ele alacağız.

Nitelikler de aslında Reflection'un bir parçasıdır ve çalışma zamanında uygulanan nesneye özellik katarlar. Başka bir deyişle nitelikler bir sınıf, yapı, metot ve benzerleri için bir metadata tanımlarlar. Bir class yapısı düşünün, bir özelliği olsun ve bu özelliği istediğimiz zaman kullanabilelim. İlk bakıldığında bu iş için properties'ler uygun gibi görünebilir ama bu tek bir özellik için o kadar da şık bir kullanım olmayacaktır. Hem kullanıcı her nesne oluşturduğunda bu özelliğe atama yapmayı unutabilir hem de reflection yapısı için uygun bir tercih olmayacaktır. İşte her bir yapının sahip olabileceği bu özellikler nitelikler (attributes) ile çok kullanışlı hale gelmektedir.

Bir nitelik, System.Attribute'den türemiş olmalıdır. Yani siz bir nitelik tanımlamak isterseniz, bu niteliğinizi her koşulda System.Attribute'den türetmelisiniz. Bir nitelik sınıfı tanımlarken de AttributeUsage adlı nitelik yazılmalıdır. AttributeUsage bu niteliğin hangi öğelere uygulanabileceğini tanımlar.

Aşağıda, bulunduğu öğeyi tarif eden özellikte basit bir nitelik örneği mevcuttur:

using System; [AttributeUsage(AttributeTargets.All)] public class MyAttribute : Attribute { public string AttributeParameter1 {get; set; } string strExplain; public MyAttribute(string _strExplain) { strExplain = _strExplain; AttributeParameter1 = "My attribute parameter value"; } public string Explain { get { return strExplain; } } } [MyAttribute("My attribute explain", AttributeParameter1="Dışardan parametre")] public class DenemeAttribute { //...... } public class UseAttribute { static void Main(string[] args) { Type t = typeof(DenemeAttribute); Console.WriteLine(t.Name + "'in nitelikleri"); object[] attributes = t.GetCustomAttributes(false); foreach (object o in attributes) { Console.WriteLine(o); } //Niteliği okuyalım Type tAttribute = typeof(MyAttribute); MyAttribute ma = (MyAttribute)Attribute.GetCustomAttribute(t, tAttribute); Console.WriteLine(ma.Explain);//Konumsal parametre Console.WriteLine(ma.AttributeParameter1);//Adlandırılmış parametre Console.Read(); } }

Bu programın çıktısı aşağıdaki gibidir:

DenemeAttribute'in nitelikleri
MyAttribute
My attribute explain
Dışardan parametre

Bu programda neler yaptık inceleyelim:

Öncelikle; MyAttribute adında bir attribute oluşturduk.

[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute

"AttributeTargets.All" ifadesi attributenin uygulandığı classın bütün elemanlarına uygulanacağı anlamına gelir. Bunu sadece istediğiniz yapılara (class, constructor, method.. vs) uygulanacak şekilde uyarlayabilirsiniz. Attribute tanımının son ekinin "Attribute" olması zorunlu olmamakla birlikte tavsiye edilir ama bu attributeyi bir yapıya tahsis ederken "Attribute" son ekini vermesek de olur. Yani aşağıdaki iki tanım da geçerlidir:

[MyAttribute("My attribute explain")] public class DenemeAttribute { //...... } veya [My("My attribute explain")] public class DenemeAttribute { //...... }

iki tanım da geçerlidir

Attribute'u tanımlarken constructor ile konumlanmış parametreye dikkat ediniz, sadece get; olarak verilmiştir. Buna konumsal parametre denir ve attribute verirken parantezi takiben ilk verilen parametre grubudur. Sonra AttributeParameter1 propertiesi tanımlandı. Buna da adlandırılmış parametre denir ve bir yapıya attribute verirken bu parametre dışardan set edilebilir.

Nitelikler hakkında genel hatlarıyla bir bilgi vermiş olduk.

Faydalı olması dileğiyle..





Paylaş:

C# Reflection (Yansıma) - Yazı Serisi - 2


Merhaba, bir önceki yazımızda Runtime Type Identification - RTTI konusunu ele almıştık. Bu yazımızda da C#'ın en güçlü özelliklerinden biri olan Reflection konusunu inceleyeceğiz.

Reflection Nedir?


Reflection, bir programın runtime sırasında dinamik olarak tip oluşturması, yüklemesi ve kullanması işlemidir. Reflection'a neden ihtiyaç var derseniz; daha büyük projeler yaptıkça göreceksiniz ki, daha dinamik, daha esnek yapılara ihtiyaç duyacaksınız. Bu ihtiyaçlardan biri de Reflection'dur. Reflection, çalışma zamanında (runtime) bir tip hakkında bilgi edinebilmemizi ve bu tipin özelliklerine göre davranabilmemizi sağlar. Bu özellik C#'ın çok güçlü bir özelliğidir. Yansıma denmesinin sebebi, reflection ile bir tipin aynadaki yansıması benzetmesidir. Burda ayna görevi gören yapı Type nesnesidir. Örneklerle konuyu detaylandırmaya çalışacağız.

Reflection'un en önemli yapılarından birisi System.Type nesnesidir. Yukarda bahsettiğimiz bir tip hakkında bilgi edinme işlemi System.Type nesnesi sayesinde yapılır. Type nesnesi System.Reflection.MemberInfo adında bir abstract sınıftan türemiştir.

MemberInfo önemli olduğundan, sahip olduğu özellikleri aşağıda belirtelim:

Type DeclaringType: Üyenin tanımlandığı class veya interfacein tipini elde eder
MemberTypes MemberType: Üyenin türünü elde eder. Örneğin; özellik mi, metot mu gibi bilgileri elde eder.
int MetaDataToken: Metadata iliştirilmiş bir değeri elde eder.
string Name: Üyenin adını elde eder.
Type ReflectedType: Reflection yapılan nesnenin tipini elde eder.

Bunların yanında MemberInfo'nun nitelikler (attributes) ile ilgili iki tane de abstract metodu vardır: GetCustomAttributes() ve IsDefined(). İlki, adından da anlaşılacağı üzere ilgili nesneye iliştirilmiş attributelerin listesini getirir, diğeri de ilgili nesne için tanımlanmış attribute var mı yok mu diye kontrol eder. Nitelikler konusunu bir sonraki yazıda ele alacağız.

Bunun gibi daha birçok metot ve özellik vardır. Hepsini burda ele almak konuyu haddinden fazla uzatacaktır. Hepsi de ismiyle müsemma anlaşılabilir cinstendir. Örneğin; reflectionda kullanılmış bir GetParameters() metodu görürseniz bilin ki bu metodun parametrelerini getirecektir.

Şimdi gelelim bu öğelerin kullanımına.. Bir örnekle başlayalım:

using System; using System.Reflection; class MyClass { int x; int y; public MyClass(int i, int j) { this.y = j; this.x = i; } public int Sum() { return x + y; } public bool IsBetween(int i) { if (x < i && i < y) return true; return false; } public string DummyMethod(int a, int b) { return "Bu da 2 parametreli metot"; } public void Show() { Console.WriteLine("x değeri: " + x + " y değeri: " + y); } } class ReflectionTest { static void Main() { Type t = typeof(MyClass); //Burda MyClass'ın görüntüsü alınır MethodInfo[] mi = t.GetMethods();//Burda MyClass'ın metot listesi getirilir Console.WriteLine("Nesnenin adı: " + t.Name); Console.WriteLine("Nesnenin desteklediği metotlar:"); foreach (MethodInfo m in mi) { //Metot dönüş tipi ve adı ParameterInfo[] pi = m.GetParameters(); Console.WriteLine("Metot Adı: " + m.Name + "Dönüş tipi: " + m.ReturnType.Name); //Parametre varsa parametrelerini göster if (pi.Length <= 0) Console.WriteLine("Parametre yok"); for (int i = 0; i < pi.Length; i++) { Console.Write(i + 1 + ". parametre: Dönüş Değeri: " + pi[i].ParameterType.Name + " Adı: " + pi[i].Name + "\n"); } Console.WriteLine(); } Console.Read(); } }

Bu programın çıktısı aşağıdaki gibi olacaktır:

Nesnenin adı: MyClass
Nesnenin desteklediği metotlar:
Metot Adı: SumDönüş tipi: Int32
Parametre yok

Metot Adı: IsBetweenDönüş tipi: Boolean
1. parametre: Dönüş Değeri: Int32 Adı: i

Metot Adı: DummyMethodDönüş tipi: String
1. parametre: Dönüş Değeri: Int32 Adı: a
2. parametre: Dönüş Değeri: Int32 Adı: b

Metot Adı: ShowDönüş tipi: Void
Parametre yok

Metot Adı: ToStringDönüş tipi: String
Parametre yok

Metot Adı: EqualsDönüş tipi: Boolean
1. parametre: Dönüş Değeri: Object Adı: obj

Metot Adı: GetHashCodeDönüş tipi: Int32
Parametre yok

Metot Adı: GetTypeDönüş tipi: Type
Parametre yok
.........................

Bu örnekte neler yaptık inceleyelim: Öncelikle MyClass'ın görüntüsünü elde ettik. Bunu Type t = typeof(MyClass) ile yaptık. Artık t nesnesi MyClass'ın sahip olduğu özelliklere sahiptir. Ama bu new ile oluşturulan nesnelere benzemez. Bu işlem, reflectionda kullanılabilecek bir tip oluşturur. Zaten amacımız MyClass nesnesi oluşturmak olsaydı bunu şu şekilde yapardık: MyClass mc = new MyClass(); ama amacımız bu değil, amacımız runtime sırasında istediğimiz tipi elde etmek. Type ile MyClass'ın görüntüsünü elde ettikten sonra, sahip olduğu metotları listeledik (MethodInfo[] mi = t.GetMethods()). Daha sonra her bir metodun parametrelerini aldık (ParameterInfo[] pi = m.GetParameters()). Program adım adım incelenirse, gayet anlaşılır ve kolay niteliktedir.

Yukardaki örnekle ilgili son olarak, çıktıya dikkat ederseniz MyClass içinde tanımlanmayan ToString, Equals.. gibi metotlar gözükmekte. Bunun nedeni, bütün tiplerin kalıtım yoluyla object'ten türetilmiş olmasıdır.

GetMethods()'un başka bir kullanım şekli daha var:

MethodInfo[] GetMethods(BindingFlags bindingAttr).

BindingFlags enumerator tipindedir. En çok kullanılan tipler ise şunlardır:

DeclaredOnly: Yalnız belirtilen class tarafından tanımlanan metotları getirir. Kalıtım yoluyla aktarılan metotları getirmez.
NonPublic: nonpublic metotları getirir.
Public: public metotları getirir.
Static: static metotları getirir.

Bu kullanım şeklinde birden fazla tipi aynı anda kullanmamız mümkündür. Bu durumda aralarına "|" işaretini koymak gerekiyor. Bunun faydalarından biri de object'ten gelen metotların ya da kalıtım yoluyla gelen başka metotların listesini almamak içindir.

Yukardaki ilgili satırı aşağıdaki gibi değiştirin:

MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public);

Çıktı aşağıdaki gibi olacaktır:

Nesnenin adı: MyClass
Nesnenin desteklediği metotlar:
Metot Adı: SumDönüş tipi: Int32
Parametre yok

Metot Adı: IsBetweenDönüş tipi: Boolean
1. parametre: Dönüş Değeri: Int32 Adı: i

Metot Adı: DummyMethodDönüş tipi: String
1. parametre: Dönüş Değeri: Int32 Adı: a
2. parametre: Dönüş Değeri: Int32 Adı: b

Metot Adı: ShowDönüş tipi: Void
Parametre yok
.........................

Görüldüğü üzere object metotları gelmemiş oldu.

Olay basit aslında. Type nesnesine MyClass'ın görüntüsünü alsın diye bir görev yükledik (Type t = typeof(MyClass)). Artık burdaki nesne MyClass'ın görüntüsünü elde edecektir ve sahip olduğu tüm metotları elde edebilecek ve kullanabilecektir. Programcıkta görüldüğü üzere, t nesnesi MyClass'ın bütün özelliklerini kendine yükledi.

Şimdiiiii gelelim bu yazıyı merakla okuyanların daha da merak edeceği bir şeye: Metotları reflection ile çağırmak..

Metotları Reflection İle Çağırma


Yukardaki örneklerde bahsettiğimiz gibi Type ile bir tipin metotlarını elde ettikten sonra Invoke() ile bu metotları çağırabiliriz. Invoke()'nin prototipi aşağıdaki gibidir:

object Invoke(object nesne, object[] argümanlar)

Bunu bir örnekle inceleyelim: Örneğin uzunluğuna takılmayın, yukardaki koda birkaç satır daha ilave ettik. Kopuk olmasın diye ya da bunu uygulamak isteyen biri hepsini alıp kullanabilsin diye tekrar tekrar yazdım:

using System; using System.Reflection; class MyClass { int x; int y; public MyClass(int i, int j) { this.y = j; this.x = i; } public int Sum() { return x + y; } public bool IsBetween(int i) { if (x < i && i < y) return true; return false; } public void Set(int a, int b) { Console.WriteLine("Set(int, int) metodu çağrıldı"); x = a; y = b; Show(); } public void Set(double a, double b) { Console.WriteLine("Set(double, double) metodu çağrıldı"); x = (int)a; y = (int)b; Show(); } public void Show() { Console.WriteLine("x değeri: " + x + " y değeri: " + y); } } class InvokeTest { static void Main() { Type t = typeof(MyClass); MyClass mc = new MyClass(10, 20); int res; Console.WriteLine(t.Name + " metotları Reflection ile çağrılıyor..."); Console.WriteLine("Örnek: MyClass mc = new MyClass(10, 20)"); Console.WriteLine(); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { //Metodun parametrelerini alalım ParameterInfo[] pi = m.GetParameters(); if (m.Name.Equals("Set", StringComparison.Ordinal) && pi[0].ParameterType == typeof(int)) { object[] args = new object[2]; args[0] = 3; args[1] = 4; m.Invoke(mc, args); } else if (m.Name.Equals("Set", StringComparison.Ordinal) && pi[0].ParameterType == typeof(double)) { object[] args = new object[2]; args[0] = 6.2; args[1] = 9.7; m.Invoke(mc, args); } else if (m.Name.Equals("Sum", StringComparison.Ordinal)) { res = (int)m.Invoke(mc, null); Console.WriteLine("Sum metodu çağrıldı. Sonuç: " + res); } else if (m.Name.Equals("IsBetween", StringComparison.Ordinal)) { object[] args = new object[1]; args[0] = 13; bool isBetween = (bool)m.Invoke(mc, args); if (isBetween) Console.WriteLine("IsBetween metodu çağrıldı. 13 sayısı 10 ile 20 arasındadır"); } else if (m.Name.Equals("Show", StringComparison.Ordinal)) { m.Invoke(mc, null); } } Console.Read(); } }

Bu programın çıktısı aşağıdaki gibi olacaktır:

MyClass metotları Reflection ile çağrılıyor...
Örnek: MyClass mc = new MyClass(10, 20)

Sum metodu çağrıldı. Sonuç: 30
IsBetween metodu çağrıldı. 13 sayısı 10 ile 20 arasındadır
Set(int, int) metodu çağrıldı
x değeri: 3 y değeri: 4
Set(double, double) metodu çağrıldı
x değeri: 6 y değeri: 9
x değeri: 6 y değeri: 9

Görüldüğü üzere; Invoke() metodu ile çalışma sırasında dinamik olarak değer atamaları yaptık ve ardından bu metotları çağırdık. Burda dikkat etmeniz gereken bir nokta da, aşırı yüklenmiş Set metodunun ilgili versiyonunu (Set(int a, int b) ve Set(double a, double b)) bulmak için parametre tipi kontrolü yapılması gerektiği. İlgili kontroller aşağıdaki satırlarda gösterilmiştir:

if (m.Name.Equals("Set", StringComparison.Ordinal) && pi[0].ParameterType == typeof(int)) if (m.Name.Equals("Set", StringComparison.Ordinal) && pi[0].ParameterType == typeof(double))

Type ile Yapılandırıcıları (Constructor) Çağırmak


Yukardaki örneklerde reflection ile metotları nasıl elde edip nasıl kullanılacağını anlattık. Fakat bu örnekler metotları çağırmak dışında bir işe yaramamıştı. Çünkü zaten nesneyi biz yapılandırmıştık. Reflection'un esas gücü bir nesne runtime sırasında oluşturulduğunda (ilk yapılandırma) ortaya çıkar. Tabiki bunun için yapılandırıcıları da elde etmemiz gerekecektir. Bunun için, tahmin edeceğiniz üzere Type'ın GetConstructors() metodunu çağırmamız gerekiyor. İlgili constructor bulunduktan sonra, çalıştırmak için yine Invoke() metodunu kullanacağız. Constructoru çağırarak ilk değer atamaları yapıldıktan sonra ilgili metodu yine benzer yöntemle çağıracağız.

Aşağıdaki örneği inceleyelim:

using System; using System.Reflection; class MyClass2 { int x; int y; public MyClass2(int a, int b) { this.x = a; this.y = b; } public int Sum() { return x + y; } } class InvokeConstructorTest { static void Main() { Type t = typeof(MyClass2); int res; //Constructor bilgilerini al Console.WriteLine(t.Name + "'in yapılandırıcıları:"); ConstructorInfo[] ci = t.GetConstructors(); foreach (ConstructorInfo c in ci) { //Dönüş tipi ve adını gösterelim Console.Write(t.Name + "("); ParameterInfo[] pi = c.GetParameters(); for (int i = 0; i < pi.Length; i++) { Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name); if (i < pi.Length - 1) Console.Write(", "); } Console.WriteLine(")"); } //Biz 2 parametreli yapılandırıcıyı arıyor olacağız int x;//bu değişkende, aradığımız constructorın indexi tutulacak for (x = 0; x < ci.Length; x++) { ParameterInfo[] pi = ci[x].GetParameters(); if (pi.Length == 2) break; } //Buraya aranan constructor bulunmadığı durumda //programdan çık gibi kontroller eklenebilir //Şimdi de nesneyi yapılandıralım object[] constParams = new object[2]; constParams[0] = 10; constParams[1] = 20; object ob = ci[x].Invoke(constParams); Console.WriteLine("Nesne, constructor(10, 20) ile yapılandırıldı..."); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { ParameterInfo[] pi = m.GetParameters(); if (m.Name.Equals("Sum", StringComparison.Ordinal)) { Console.WriteLine("Sum() metodu çağrılıyor..."); res = (int)m.Invoke(ob, null); Console.WriteLine("Sonuç bulundu: " + res); } } Console.Read(); } }

Bu programın çıktısı aşağıdaki gibi olacaktır:

MyClass2'in yapılandırıcıları:
MyClass2(Int32 a, Int32 b)
Nesne, constructor(10, 20) ile yapılandırıldı...
Sum() metodu çağrılıyor...
Sonuç bulundu: 30

Bu örneği incelersek; ilk önce yapılandırıcı listesini elde ettik. 2 parametreli yapılandırıcıyı bildiğimizden 2 parametreli olanı bulduk (daha karmaşık yapılandırılar için önceki örneklerde olduğu gibi parametre tipi kontrolü yapılarak ilgili yapılandırı bulunabilir). Sonra bu yapılandırıcıya runtime zamanında değer atadık ve nesneyi yapılandırdık. Sonra ilgili metodu da aynı yöntemle bularak, yapılandırıcı ile yapılandırdığımız nesnenin metodunu çağırdık.

Assembly'leri Reflection'da Kullanmak


Buraya kadar hep açık kodunu bildiğimiz yapılar için reflection kullandık. Fakat reflectionun tam gücü assembly'lerin kullanımında ortaya çıkmaktadır. Bildiğiniz üzere bir assembly, içerdiği classlar, yapılar, metotlar ve parametreler gibi birçok bilgiyi beraberinde taşır. Bu da reflection kullanımı için son derece uygundur. Bu şekilde dışardan eklenen bir assembly için sık sık tanımlama gereği duymadan reflection ile dinamik ve esnek olarak kullanabiliriz. Örneğin; bir sistemdeki tüm tipleri araştıran ve görsel olarak ortaya koyan bir "tip tarayıcısı" gibi bir programınızın olduğunu düşünelim. Bu noktada reflection kullanmak işinizi son derece kolaylaştıracaktır.

Assembly'lerin kullanımına bir örnek yapalım. Öncelikle birkaç classtan oluşan bir program yazıp derleyeceğiz. Sonra bu programın derlenmesi sonucu oluşan exe'yi runtime sırasında yükleyip kullanacağız.

Basit olması açısından bir proje oluşturup 3 tane class yerleştirip derleyelim. Derlendikten sonra dizinin altında bir exe oluşacaktır. Bu exe'nin adını belirtip ilgili tipleri elde ettikten sonra yine aynı yöntemlerle kullanacağız:

using System; using System.Reflection; public class MyClass { int x; int y; public MyClass(int a, int b) { this.x = a; this.y = b; } public int Sum() { return x + y; } } public class OtherClass { public string Show() { return "Bu da diğer class"; } } public class UseAssemblyReflection { static void Main() { int res; Assembly asm = Assembly.LoadFrom("ReflectionAssembly.exe"); Type[] types = asm.GetTypes(); Console.WriteLine("ReflectionAssembly.exe'deki tiplerin listesi"); foreach (Type temp in types) { Console.WriteLine(temp.Name); } Type t = types[0];//MyClass'ı alıyoruz Console.WriteLine("\nMyClass'ın Constructorları listeleniyor"); ConstructorInfo[] ci = t.GetConstructors(); foreach (ConstructorInfo c in ci) { //Dönüş tipi ve adını gösterelim Console.Write(t.Name + "("); ParameterInfo[] pi = c.GetParameters(); for (int i = 0; i < pi.Length; i++) { Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name); if (i < pi.Length - 1) Console.Write(", "); } Console.WriteLine(")"); } //Biz 2 parametreli yapılandırıcıyı arıyor olacağız int x;//bu değişkende, aradığımız constructorın indexi tutulacak for (x = 0; x < ci.Length; x++) { ParameterInfo[] pi = ci[x].GetParameters(); if (pi.Length == 2) break; } //Buraya aranan constructor bulunmadığı durumda //programdan çık gibi kontroller eklenebilir //Şimdi de nesneyi yapılandıralım object[] constParams = new object[2]; constParams[0] = 10; constParams[1] = 20; object ob = ci[x].Invoke(constParams); Console.WriteLine("\nNesne, constructor(10, 20) ile yapılandırıldı..."); MethodInfo[] mi = t.GetMethods(); foreach (MethodInfo m in mi) { ParameterInfo[] pi = m.GetParameters(); if (m.Name.Equals("Sum", StringComparison.Ordinal)) { Console.WriteLine("Sum() metodu çağrılıyor..."); res = (int)m.Invoke(ob, null); Console.WriteLine("Sonuç bulundu: " + res); } } Console.Read(); } }

Bu programın çıktısı aşağıdaki gibi olacaktır:

ReflectionAssembly.exe'deki tiplerin listesi
MyClass
OtherClass
UseAssemblyReflection

MyClass'ın Constructorları listeleniyor
MyClass(Int32 a, Int32 b)

Nesne, constructor(10, 20) ile yapılandırıldı...
Sum() metodu çağrılıyor...
Sonuç bulundu: 30


Ben projemi oluştururken ReflectionAssembly adını vermiştim. Bu yüzden ilgili exe'yi belirtirken "ReflectionAssembly.exe" olarak belirttim. Assembly'nin yüklenme şekline dikkat ediniz:


Assembly asm = Assembly.LoadFrom("ReflectionAssembly.exe");

asm nesnesine ilgili exe'nin bilgileri yüklendikten sonra tipleri elde etmek için aşağıdaki satırı kullandık.

Type[] types = asm.GetTypes();

Bundan sonrası yine yukarda anlattığımız yöntemlerle aynı.

Not: Burda unutulmaması gerekir ki, yüklenmesi gereken assembly'nin türü illa ki exe olması gerekmez. Projenin türüne göre dll de olabilir. dll olması durumunda Add > Reference ile referans eklendikten sonra direk ilgili dll'in adı verilebilir (ObjectDeneme.dll gibi) ya da referans eklemeden direk path olarak da verilebilir (C:\users\......\ObjectDeneme.dll gibi)

Reflection hakkında bu kadar bilgi verdikten sonra diyeceksiniz ki, biz zaten özelliklerini bildiğimiz dll veya exe'leri kullandık. Zaten daha önce de belirtmiştik bir assembly beraberinde tiplerin özelliklerini de taşır. Dolayısıyla bu normal bir durumdur. Fakat hiç özelliklerini bilmediğiniz tiplerin de reflection ile özelliklerini açığa çıkarmanız mümkündür. Yukardaki kodları, hayal gücünüze bağlı olarak daha dinamik hale getirip aynı anda sistemdeki bütün tiplerin özelliklerini listeleyebilirsiniz.

Reflection konusunun sonuna geldik. Bir sonraki yazımızda Nitelikler konusunu ele alacağız.

Umarım faydalı olabilmişimdir... Hepinize iyi günler dilerim..


Paylaş:

C# Runtime Type Identification - RTTI - Yazı Serisi - 1


Merhaba,

Bu yazı serisinde C#'ın, dinamik yapılar inşa etmede çok güçlü 3 konusundan bahsedeceğim. Bu 3 konu birbiriyle bağlantılı olduğu için yazı serisine bağlamak mantıklı olur diye düşündüm. Konular sırasıyla aşağıdaki gibidir:

  1. Runtime Type Identification - RTTI (Çalışma Zamanı Tip Tanımlaması)
  2. Reflection (Yansıma)
  3. Attributes (Nitelikler)
Bu 3 kavram da çalışma zamanında (runtime) nesne yapılandırmayla ilgilidir. Basit bir programda runtime sırasında tip tanımlamaya pek ihtiyaç duyulmaz fakat programlar büyüdükçe kontrol edilmesi güç bir hal almaktadır. Programlarınız büyüdükçe göreceksiniz ki dinamik yapılara ihtiyaç duyacaksınız. Bir program ne kadar esnek, ne kadar dinamik olursa o kadar güçlüdür, o kadar değerlidir.

Lafı çok uzatmadan birinci maddeden başlayalım. (Bu yazıda sadece birinci maddeden bahsedeceğim. Diğer konular için başlıkların üzerindeki linklere tıklayabilirsiniz). Çalışma zamanı tip tanımlaması (Runtime Type Identification - RTTI), adından da anlaşılacağı üzere bir programın çalışması sırasında bir tipin tanınmasına imkan verir. Bunun ne gibi faydaları var derseniz, bir temel sınıfın ne tür bir nesneye referans verdiğini belirlemek ya da tip atamalarının geçersiz olup olmadığını anlamaktır. RTTI aynı zamanda Reflection ile de ilgilidir. RTTI'nın 3 önemli anahtar sözcüğü vardır: is, as ve typeof. Bunları biraz daha açalım.


is anahtar sözcüğü


Bir nesnenin bir tipte olup olmadığını kontrol eder. Eğer tip uyumu varsa true yoksa false döner.

Aşağıdaki örneği inceleyelim:

using System; class A { } class B : A { } class Program { static void Main(string[] args) { A a = new A(); B b = new B(); if (a is A) Console.WriteLine("a nesnesi A tipi ile uyumludur"); if (b is A) Console.WriteLine("b nesnesi A tipi ile uyumludur"); if (a is B) Console.WriteLine("a nesnesi B tipi ile uyumludur"); else Console.WriteLine("a nesnesi B tipi ile uyumlu değildir"); if (a is object) Console.WriteLine("a bir objedir"); Console.ReadLine(); } }

Bu kodun çıktısı aşağıdaki gibi olacaktır:

a nesnesi A tipi ile uyumludur
b nesnesi A tipi ile uyumludur
a nesnesi B tipi ile uyumlu değildir
a bir objedir


Görüldüğü üzere, is operatörü bir tip atamasının geçerli olup olmadığını tespit etmek için kullanılır. is'in genel kullanım şekli bu şekildedir.


as anahtar sözcüğü


Bazen bir tip atamasının başarısız olması durumunda exception fırlatmamasını istersiniz. Bu durumda as anahtar sözcüğü kullanılabilir. Eğer tip ataması başarılı olursa true döndürür, başarısız olursa exception fırlatmak yerine null değeri döndürülür. 

as operatörü aslında is operatörünün özelleşmiş halidir. as operatörüne örnek vermeden önce yukardaki örnek için aşağıdaki kodu inceleyelim.

if (a is B) b = (B)a; else b = null;

as operatörü aslında yukarıdaki deyimi gerçekler. Yukardaki kod bloğu aşağıdakiyle değiştirilebilir.


b = a as B;//burada tip uyumsuzluğu var. dolayısıyla a nesnesi B tipine dönüşmeyecek ve //null dönecektir. if (b == null) { Console.WriteLine("Atama gerçekleşmedi!"); }

typeof Kullanımı


as ve is operatörleri kendilerine göre kullanışlı olmalarına rağmen, sadece tip uyumluluğunu kontrol ederler. typeof ise bir tip için System.Type nesnesi yükler. Bundan sonra bu tip hakkında Type tarafından tanımlanan çeşitli özellik ve metotları kullanarak bu tip hakkında bilgi elde edilebilir. Reflection kısmında bunun önemi daha da anlaşılacaktır. Reflection kısmında daha detaylı incelenecektir ama kısaca bilgi vermek gerekirse; Type üç önemli özelliği kullanabilir: FullName, IsClass, IsAbstract. Bunlara kısaca bir göz atalım:

using System; using System.IO; class TypeOfKullanimi { static void Main(string[] args) { Type t = typeof(StreamReader); Console.WriteLine(t.FullName); if (t.IsClass) Console.WriteLine("t bir classtır"); if (t.IsAbstract) Console.WriteLine("t bir abstract classtır"); Console.ReadLine(); } }

Bu programın çıktısı aşağıdaki gibi olacaktır:

System.IO.StreamReader
t bir classtır


Tip dönüşümleri hakkında bu bilgileri verdikten sonra bir sonraki derste C#'ın en güçlü özelliklerinden biri olan Reflection'a göz atacağız.

Umarım faydalı olabilmişimdir...


Paylaş:

Bu Blogda Ara