18 Ocak 2018 Perşembe

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ş:

1 Ocak 2018 Pazartesi

C#'ın Tarihçesi, .NET Framework ve Common Language Runtime (CLS)


Merhaba, bu yazıda C, C++ ve C#'ın kısa bir tarihçesinden bahsedeceğim. Ardından .NET Framework ve derleme zamanında neler yaşandığından, Common Language Runtime (CLS)'in görevinden kısaca bahsedeceğim.

C#'a geçmeden önce günümüzün nesneye yönelik programlama dilleri C# ve Java'nın ortaya çıkmasında büyük rolü olan C ve C++ dillerinin tarihçesinden de kısaca bahsetmek istiyorum.

C Dili: Modern Programlama Çağının Başlangıcı

Daha önceleri Pascal Programlama Dili kayda değer bir başarı elde etmiş olsa da günümüzün programlama dillerinin şekillenmesinde ana model C Programlama Dili olmuştur.  

C dili, 1960'ların yapısal programlama (structured programming) devrimi sonucu ortaya çıkmıştır. 1970 yılında ABD'li ünlü bilgisayar mühendisi Dennis Ritchie (1941 - 2011) tarafından icat edilmiştir.

Yapısal programlama dillerinden önce program yazmak oldukça zordu. Kodlar "spagetti kod" denilen atlamalar (go to ifadesini çoğu programcı hatırlar), çağrılar ve döngülerden oluşan, takibi zor program yapılarından ibaretti. Yapısal programlama ise bu karışıklıklardan (spagetti kod vb) kurtulmak için ortaya atıldı. C ile beraber birçok yapısal dil mevcuttu fakat C'nin estetik, esnek, sade ve performanslı olması bu dillerin önüne geçti ve böylece 1980'lerin en yaygın kullanılan yapısal dili oldu. 

C dilinin bütün esnekliğine rağmen kısıtlayıcı yönleri de mevcuttu. C'nin en büyük problemlerinden birisi de programlar büyüdükçe kontrol edilmesi zor bir hal almasıydı. Programlar belli bir büyüklükten sonra esneklikten çıkıp, karmaşık ve yönetilemez bir hal alıyordu. Her ne kadar iyi programlanıyor olursa olsun C dili, belli bir büyüklüğe ulaştıktan sonra mutlaka bu engele çarpacaktı. İşte bu noktada C++ dili imdada yetişmiştir.

C++'ın Doğuşu ve Nesneye Yönelik Programlama

Yukarda bahsettiğimiz program büyüklüğü ve karmaşıklığı sonrasında çözüm olarak Object Oriented Programming (OOP) denilen programlama yapısı ihtiyacı ortaya çıktı. 

C++ ilk olarak 1979 yılında Bjarne Stroustrup tarafından ortaya kondu. C++, C'nin üzerine inşa edilerek nesne yönelimli özelliği eklenmiştir. Dolayısıyla C++, C'nin bütününü içermektedir. Bahsettiğimiz karmaşıklığı önlemek ve nesneye yönelik programlama dili olması itibariyle 10 yıllık bir süreçten sonra 1990 yılında C++ en yaygın kullanılan dil haline gelmiştir. Günümüzde bile hala bazı yüksek performas gerektiren konularda C++ kullanılmaktadır. 

C++ tamamiyle yeni bir dil ortaya atmak için çıkarılmasa da C# ve Java gibi dillerin ortaya çıkmasında büyük bir atılım olmuştur. 

C#'ın Doğuşu

Bilindiği üzere Java, platform bağımsızlık ilkesine dayanarak C ve C++ dillerinden türemiştir. Java her ne kadar platform bağımsız bir dil olsa da bazı özellikleri açısından yetersiz kaldığı durumlar olmuştur. Bunlardan bir tanesi; karışık dillerle birlikte çalışabilme becerisidir. Bir diğeri de, Windows platformları entegrasyonudur. Her ne kadar JVM yüklü bir makinede Java sorunsuz çalışabiliyor olsa da Java Windows'a tam hakim değildir. Dünyada en çok kullanılan işletim sistemi olan Windows için doğrudan destekten yoksun olması Java'nın bir dezavantajı olarak karşımıza çıkmaktadır. 

Yukarda bahsettiğim nedenlerden dolayı Microsoft, kendi yağında kavrulmak istedi ve C#'ı geliştirdi. 1990'ların başında .NET temelleriyle birlikte C# şekillenmiş olsa da, Microsoft ilk kez 2000'li yılların ortalarında C# alfa versiyonunu piyasaya sürmüştür. İlk sürümünden sonra çok hızlı gelişti ve Microsoft'un vazgeçilmez programlama dili haline geldi. Çıktığı ilk günden günümüze kadar her dönemin ihtiyaçlarına yönelik sayısız çözümler ve tasarımlar sundu. Bu sayede hiçbir zaman popülaritesi düşmedi.

C#'ın baş mimarı Anders Hejlsberg'tir, Bill Gates değildir :) Genelde Microsoft'un bütün araçlarının yazarı Bill Gates olduğu kanısı vardır ya onun için ayrıca buna dikkat çekmek istedim. Anders Hejlsberg aynı zamanda Turbo Pascal'ın da mimarıdır. 

C# öğrenilmesi kolay, sade ve çok esnek bir dildir. Aynı zamanda .NET'in gözbebeği ve çok şık bir dildir. Oldukça güçlüdür. OOP'un bütün güçlü nimetlerini kapsamlı bir şekilde ve eksiksiz olarak kullanır. .NET'in en önemli silahıdır. Microsoft, en büyük yatırımı C# için yapmıştır. 

C# ve Java'nın ataları C ve C++'tır. C# ve Java'nın temelleri bu iki dil üzerine kurulmuş olduğundan C# ve Java birer kuzen gibidir. Bu nedenle ortak yönleri ve benzerlikleri oldukça fazladır. Bu vesileyle C#'ı OOP yönüyle öğrenen birisi rahatlıkla Java'yı da öğrenebilir.

C#'ın Gelişimi ve Sürümleri

Kısaca bahsetmek gerekirse;

C# 1.0 ilk sürümdür. 1.0'dan kısa bir süre sonra 1.1 sürümünü yayınlamıştır. Bu iki sürüm arasında küçük farklılıklar bulunmaktadır fakat 2.0 için durum tamamen farklıdır. 

C# 2.0 önemli yenilikler eklemiştir. Bunlardan bazıları: jenerikler, anonim metotlar ve kısmi sınıf tanımlamaları (partial class

C# 3.0 da büyük yenilikleri getirdi. Bunlardan bazıları: Lambda deyimleri, LINQ (Language Integrated Query), extension metotları ve belirtilmemiş tipte değişkenlerdir. (Bu kavramlara ayrıca başka yazılarda değineceğim)

C# 4.0 ile gelen en önemli özelliklerden biri de dinamik tiplerdir (daha sonra değineceğim). Bir diğer önemli özellik de Task Parallel Library (TPL) ve Parallel LINQ (PLINQ) şeklinde özetlenebilir.

.NET Framework Nedir?

.NET Framework farklı dillerin (örneğin; C#, VB.NET gibi) birlikte çalışmasına olanak tanır ve Windows platformu için program taşınılabilirliği sağlar. Örneğin; C# ve VB.NET ile ayrı ayrı yazılmış uygulamaları uygun .NET Framework sürümü kurulu bir makinede rahatlıkla çalıştırabilirsiniz. .NET Framework'un sürümleri arasında bir hiyerarşi söz konusudur. En tepedeki sürüm bütün sürümleri çalıştırabilmektedir. .NET Framework, Windows kurulumu ile birlikte gelmekte, ayrıca sonradan da yüklenebilmekte veya güncellenebilmektedir. .NET Framework'u (Java Virtual Machine) JVM'ye benzetebiliriz. 

Common Language Runtime (CLR) Nedir?

Common Language Runtime (Ortak Dil Çalışma Zamanı) .NET kodunun çalışmasını kontrol eder. CLR'nin çalışma yapısı şu şekildedir: Bir C# kodu derlendiğinde, elde edilen çıktı, direk çalıştırılabilir kod olmayıp MSIL (Microsoft Intermediate Language) denilen bir çıktı üretir. MSIL bir sözde kodtan (pseudo code) ibarettir. Bu kod, CPU'dan bağımsız bir Assembly dili demektir.  CLR ise bu elde edilen çıktının çalıştırılabilmesini sağlar. Bu şu demek oluyor: MSIL çıktısı elde edilecek şekilde derlenen her program CLR'ın uygulandığı her ortamda çalışabilir demektir. Bu da .NET Framework'un taşınılabilirliğini sağlamaktadır. 

Bir sonraki makalede görüşünceye dek hoşçakalın...



Paylaş:

22 Aralık 2017 Cuma

Visual Studio "Create Unit Tests" Menüsü


Visual Studio'da Unit Test yapmak istiyorsunuz fakat "Create Unit Tests..." menüsü gelmiyorsa ya da disable geliyorsa bunun çözümü için doğru yerdesiniz.

Eğer kodunuza sağ tıkladığınızda "Create Unit Tests" gelmiyorsa öncelikle bunun gelmesini sağlamalıyız.

1) Visual Studio > TOOLS > Customize

2) Açılan Customize ekranında Commands sekmesine gelin. Context menu radio butonunu tıklayın. Combodan "Editor Context Menus | Code Window" seçiniz. Gelen seçeneklerden "Create Unit Tests..." menüsünü bulun ve seçin. Sonra Close deyin.



Bu durumda kodunuzun her hangi bir yerinde sağ tıkladığınızda "Create Unit Tests..." menüsü gelecektir ama henüz test projeniz olmadığından aşağıdaki gibi disabled durumda gelebilir.


Yukardaki gibi disabled durumda geliyorsa test projesi olmadığındandır. Bu proje için bir test projesi oluşturmanız gerekmektedir.

1) FILE > Add > New Project deyip çıkan ekranda Installed > Visual C# > Test menüsünden yeni bir test projesi ekliyoruz.



2) Eklenen test projesine sağ tıklayıp Add > Unit Test diyoruz. 


Unit test yapmak istediğiniz kod sayfasına gelip sağ tıklayınca artık "Create Unit Tests..." menüsü aşağıdaki gibi enable olacaktır.



Şimdilik bu kısmını anlattık. Unit Test nedir nasıl yapılır konusunu ise başka bir yazıda ele alacağız.

Herkese iyi kodlamalar...


Paylaş:

Bu Blogda Ara