Tag Archives: csharp

C#’ta Alanlar (Fields)

C#’ta alanlar, bir sınıf ya da bir sınıf örneği ile ilişkilendirilmiş değişkenlerdir. Static değiştiricisi ile tanımlanmış alanlar static field olarak tanımlanır. Static field’lar, tam olarak bir bellek yeri tanımlarlar. Kaç tane sınıf örneği oluşturulursa oluşturulsun, static field’ın sadece bir tane kopyası vardır.

Önemli: Static field’lar her generic type için ayrı bir yapısal yapıdır. Örneğin elinizde;

class Stack<T>
{
public readonly static Stack<T> bos = tralalalala;
}

varsa, Stack<int>.bos ve Stack<string>.bos alanları farklı alanlardır.

Static değiştiricisiz tanımlanmış alanlar birer örnek alanlardır. Her bir sınıf örneği, o sınıfın her bir örnek alanın kopyasını içerir.

Aşağıdaki örnekte; her bir Color sınıfının örneği, her bir r (red), g (green), b (blue) alanlarına sahiptir ama sadece bir adet kopya Siyah, Beyaz, Kırmızı, Yeşil ve Mavi static (durağan) alanlar mevcuttur.

public class Color
{

public static readonly Color Siyah = new Color(0, 0, 0);
public static readonly Color Beyaz = new Color(255, 255, 255);
public static readonly Color Kırmızı = new Color(255, 0, 0);
public static readonly Color Yeşil = new Color(0, 255, 0);
public static readonly Color Mavi = new Color(0, 0, 255);

private byte r, g, b;
public Color(byte r, byte g, byte b)
{
this.r = r;
this.g = g;
this.b = b;
}
}

Yukarıda görüldüğü gibi sadece okunabilir alanlar readonly değiştiricisi ile tanımlanmıştır. Bir readonly alana atama, sadece o alanın tanımlamasındaki bir kısmından ya da aynı sınıftaki bir yapıcı (constructor) sayesinde gerçekleşebilir.

Önemli: Readonly kelimesi dışarıdaki tip yapıcısı tarafından o alanın konumunun değiştirilmesini önler fakat o konumdaki değeri korumaz. Örneğin, aşağıdaki gibi İsimler şeklinde bir sınıfımız olsun;

public class İsimler
{
public static readonly StringBuilder İlkDogan = new StringBuilder("İlker");
public static readonly StringBuilder İkinciDogan = new StringBuilder("Soner");
}

Bir yapıcının dışından direkt olarak İlkdoğan örneğinin sonucunu değiştirmek bize bir derleyici hatası verecektir.

İsimler.İlkDogan = new StringBuilder("Caner"); // Derleyici Hatası

Ama, StringBuilder örneğini modifiye ederek aynı sonuca aşağıdaki şekilde ulaşabilirim;

İsimler.İlkDogan.Remove(0,6).Append("Caner");

Console.WriteLine(İsimler.İlkDogan); // Output “Caner” olur.

Bu yüzden, read-only kullanımı değişmez tipler için sınırlı kullanılmalıdır şeklinde tavsiye edilir. Değişmez tipler (Immutable types), int, double, string gibi açıkça setter’lara (belirleyiciler) sahip değildirler.

C# Sınıflar Kavramı

Sınıflar, C# tiplerinden en temelidir. Bir sınıf, alanları (fields) ve aksiyonları (metod ve diğer fonksiyon üyelerini) basit bir birim içerisinde birleştiren veri yapılarıdır. Sınıflar, dinamik olarak oluşturulmuş örnekler için tanımlama sağlarlar. Ki bunlardan nesneler olarak bilinir. Sınıflar kalıtımı (inheritance) ve polymorphism’i destekleyen yapılardır.

Yeni sınıflar, kullanılan sınıf bildirimlerinden yaratılırlar. Bir sınıf tanımlaması, öncelikle sınıfın niteliği (modifiers), sınıfın ismi, ana sınıf (eğer varsa) ve sınıfa uygulananan arayüzlerler (interface) sıralaması ile olur. Bu tanımlamalardan sonra { ve } işaretleri ile sınıf tanımlaması tamamlanır.

Aşağıda Nokta adında basit bir sınıf tanımlaması yapılmıştır:

public class Nokta
{
public int x, y;
public Nokta(int x, int y)
{
this.x = x;
this.y = y;
}
}

New operatörü ile oluşturulmuş örnek sınıf ki yeni örnek için bellek ayrılmış, örneğe ilk kullanım için yapıcı (constructor) çağrımı yapılır ve geriye bu örneğe bir referans döndürür.

Aşağıda iki Nokta nesnesi oluşturulmuş ve bu iki nesneye refefans iki değişken içinde saklanır:

Nokta n1 = new Nokta(0, 0);
Nokta n2 = new Nokta(10, 20);

Memory bir nesnenin kullanımı ortadan kalktığında o nesnenin kullandığı alanı geri çağırır. Bir nesnenin C#’ta açıkça serbest bırakılması hem gereksiz hem de imkansızdır.

C# İfade Kavramı

C#’ta ifadeler operand ve operatör adı verilen iki elementten oluşur. Bir ifadenin operatörleri, operandlara hangi operasyonların uygulanacağını gösterir. Operatör’lere örnek olarak;  +, -, *,/, ve new anahtar kelimesi gösterilebilir. Operand’lara örnek olarak ise; alanlar (fields), yerel değişkenler ve ifadeler gösterilebilir.

Bir ifade birden fazla operatör içeriyorsa, hangi operatörün işlenileceğine o operatörün öncelik sırası karar verir. Örneğin; X + Y * Z ifadesi X + (Y * Z) olarak işlenir * (çarpma) operatörü + (toplama) operatörüne göre daha önceliklidir.

Önemli: Öncelik sırası hangi operatörlerin hangi sıra ile işleneceğine karar verir. Fakat hangi operandın önce işleneceğine karar vermez. Operand’lar soldan sağa işlenir periyodik olarak. Yukarıdaki örnekte, önce X hesaplanıldı, sonra Y, sonra Z, sonra çarpma işlemi yerine getirildi, sonra da toplama işlemi. X’in Y’den önce hesaplanmasının nedeni solda olması, çarpma işleminin toplama işleminden önce gerçekleşmesinin nedeni önceliğinin yüksek olmasıdır.

Bir çok operatör aşırı yüklenebilir (Overloading). Operatör yüklemesi, kullanıcı tanımlı operatör işlemelerinde spesifik işlemler yapılmasına olanak tanır.

Aşağıdaki tablo, C# operatörlerini yüksek öncelikten düşük önceliğe göre listeler. Aynı kategorideki operatörler eşit önceliğe sahiptir.

Kategori İfade Açıklama
Birincil x.m Üye erişimi
Birincil x(…) Metod ve delegate tanımlama
Birincil x[…] Dizi ve indexer erişimi
Birincil x++ Öncelikli arttırma
Birincil x– Öncelikli azaltma
Birincil new T(…) Nesne ve delegate oluşturma
Birincil new T(…){…} Kullanıma hazır nesne oluşturma
Birincil new {…} Anonymus nesne kullanımı
Birincil new T[…] Dizi oluşturma
Birincil typeof(T) T için mevcut System.Type tipi
Birincil checked(x) İşaretlenmiş konuda hesaplanan ifade
Birincil unchecked(x) İşaretlenmemiş konuda hesaplanan ifade
Birincil default(T) T tipinin mevcut varsayılan değeri
Birincil delegate {…} Anonymus fonksiyon (Anonymus metod)
Tekil +x Özdeşlik
Tekil -x Değil
Tekil !x Lojik değil (Not)
Tekil ~x Bit bit değil (~x = –x-1)
Tekil ++x Ön arttırma
Tekil –x Ön azaltma
Tekil (T)x X’i açık olarak T tipinde çevirme
Çoğulsal x * y Çarpma
Çoğulsal x / y Bölme
Çoğulsal x % y Mod (kalan)
Eklemeli x + y Ekleme, string birleştirme, delegate birleşimi
Eklemeli x – y Çıkarma, delegate uzaklaştırma
Öteleme x << y Sola öteleme
Öteleme x >> y Sağa öteleme
İlişkisel ve tip testi x < y Daha küçük
İlişkisel ve tip testi x > y Daha büyük
İlişkisel ve tip testi x <= y Küçük veya eşit
İlişkisel ve tip testi x >= y Büyük veya eşit
İlişkisel ve tip testi x is T Eğer x, bir T ise true döner, değilse false
İlişkisel ve tip testi x as T Eğer x, bir T tipi ise true döner, değilse false
Eşitlik x == y Eşittir
Eşitlik x != y Eşit değil
Lojik AND x & y Bit bit lojik olarak AND işlemi
Lojik XOR x ^ y Bit bit lojik olarak XOR işlemi
Lojik OR x | y Bit bit lojik olarak OR işlemi
Koşullu AND x && y Eğer x true ise y’yi hesaplar
Koşullu OR x || y Eğer x false ise y’yi hesaplar
Null Kaynaşma X ?? y Eğer x null ise y’yi hesapla, değilse x’i hesapla
Koşullu x ? y : z Eğer x true ise y’yi hesapla, x false ise z’yi hesapla
Atama ve anonymous fonksiyonlar x = y Atama
Atama ve anonymous fonksiyonlar x op= y Bileşik atama. Destekleyen operatörler: *= /= %= += -= <<= >>= &= ^= |=
Atama ve anonymous fonksiyonlar (T x) => y Anonymus fonksiyonlar (lambda expression)

C# Attribute (Nitelik) Kavramı

Bir C# programında assembly’ler, tipler, üyeler, geri dönüş değerleri, parametreler ve diğer varlıklar onların davranışlarını belirleyecek değiştiriciler desteklerler. Örneğin, bir metoda erişebilirlik durumunu public, protedted, private ve internal anahtar kelimeleri belirler. C# bunlar gibi kullanıcı tanımlı bilgileri runtime zamanında programa uygulamak için genelleştirebilir yapıya sahiptir. Program bu gibi ilave bildirim bilgilerini attribute tanımlayarak belirler.

Nitelikler bir sınıf üyesi değildir, sadece ilişkilendirildikleri üyeler için ilave bilgi sağlarlar. Aşağıdaki örnek bir YardımAttribute niteliği tanımlar. Bu nitelik programdaki üyelerin ilişkilerine bir link sağlar:

using System;

namespace ConsoleApplication1
{
public class YardımAttribute : Attribute
{
string url;
string konu;

public YardımAttribute(string url)
{
this.url = url;
}

public string Url
{
get { return url; }
}

public string Konu
{
get { return konu; }
set { konu = value; }
}
}
}

Tüm attribute sınıfları .NET Framework tarafından desteklenen System.Attribute sınıfından türetilirler. Nitelikler ilişkilendirildikleri tanımlamdan önce köşeli parantezler içerisinde nitelik ismi ve herhangi bir argümanı ile tanımlanırlar. Eğer bir niteliğin adı Attribute ile bitiyorsa, o kısmı yazılmadan o niteliğe referans gösterilebilir. Yukarıdaki örneğimiz için konuşursak şu şekilde olur;

 [Yardım("<a href="http://sonergonul.net">http://sonergonul.net")]</a>
public class Oge
{
[Yardım("<a href="http://sonergonul.net&quot;">http://sonergonul.net"</a>, Konu="Göster")]
public void Göster(string text) { }
}

Bu örnek YardımAttribute niteliğini Oge sınıfına ve başka bir YardımAttribute niteliğini de sınıfın içerisindeki Göster metoduna bağlar. Attribute sınıfının public yapıcısı, nitelik bir program varlığına bağlandığında sağlanması gereken bilgiyi kontrol eder. İlave bilgiler attribute sınıfının read-write özelliklerine referans ile sağlanır (Az önceki Konu özelliği gibi).

Aşağıdaki örnek’e bakacak olursa, yansıma kullanılarak runtime zamanında programın varlığının nitelik bilgilerine nasıl erişebileceğimizi gösterir:

class Test
{
static void YardımGöster(MemberInfo uye)
{
YardımAttribute y = Attribute.GetCustomAttribute(uye, typeof(YardımAttribute))
as YardımAttribute;

if (y == null)
{
Console.WriteLine("{0} için yardım yok", uye);
}
else
{
Console.WriteLine("{0} için yardım", uye);
Console.WriteLine(" Url={0} Konu={1}", y.Url, y.Konu);
}
}

static void Main()
{
YardımGöster(typeof(Oge));
YardımGöster(typeof(Oge).GetMethod("Göster"));
}
}

Burada GetCustomAttribute() metodunu okumak istediğimiz bir niteliğin adını bildiğimiz zamanlarda kullanırız. Ve sonra da bir niteliğie ait bir referans elde ettiğimizde o niteliğin üyelerine ulaşabiliriz. Eğer okumak istediğimiz niteliğin adını bilmiyorsak GetCustomAttributes() metodunu kullanabiliriz. Bu metod, bir nesneye bağlanmış tüm niteliklerin listesini okur. Ve aşağıdaki şekilde bu metodu kullanabilirdik;

object[] att = typeof(Oge).GetCustomAttributes(false);
foreach(object o in att)
{

Console.WriteLine(o);

}

Bir nitelik yansıma (Reflection) ile çağırıldığı zaman, attribute sınıfının yapıcısı programın sağladığı bilgiyle çağırılır ve sonuç niteliği geri döndürülür. Eğer ilave bilgiler özellikler ile sağlandıysa, bu özellikler nitelik örneği döndürülmeden önce verilen değere atanırlar.

Attribute parametreleri 2 kategoriye ayrılırlar: konumsal ve isimsel. Konumsal parametreler nitelik sınıfının yapıcısında parametre olarak bulunurlar (public YardımAttribute(string url), http://sonergonul.net). İsimsel parametreler de attribute tipinin public alan ya da özelliklerinde bulunurlar (public string Konu, Konu=”Göster” gibi). Bir nitelik belirlediğinizde, o nitelik ilişkilendirdiği temel alınan nitelik yapıcısının konumsal parametrelerini içermek zorundadır. İsimsel parametreler ise opsiyoneldir. İhtiyaca göre kullanılır ya da kullanılmazlar. Ve bunların sırası önemli değildir. İsimsel parametrelerde değer atanması gerekli değildir. Varsayılan değerleri kullanılabilir.

Bir sınıf varlığına birden fazla nitelik bağlayabiliriz. Her nitelik tek bir köşeli parantez içinde virgül ile ayrılabilir ya da hepsi kendi köşeli parantezleri içerisinde yazılabilir. Aşağıdaki 3 örnek aynı görevi görür;

   [Serializable, Obsolete, Obfuscation]
public class Sınıf { }

[Serializable, Obsolete]
[Obfuscation]
public class Sınıf { }

[Serializable]
[Obsolete]
[Obfuscation]
public class Sınıf { }

Standart Nitelikler (AttributeUsage, Conditional, Obsolete)

Eğer bir nitelik sınıfınızda, tanımlanan niteliğin hangi öğe tipler için uygulanabileceğini belirtmek istersek AttributeUsage kullanmamız gerekir. Örneğin yazımızın başındaki YardımAttribute sınıfından önce;

[AttributeUsage(AttributeTargets.Method)]

şeklinde bir tanımlama yapsaydık, bu nitelik sadece metod yapılarına bağlanabilirdi. AttributeTargets.All seçeneği ile bu niteliğin tüm yapılara bağlanabildiğini gösterebiliriz. Ya da sadece enum ve sınıf tiplerine bağlanabileceğini belirtmek için aşağıdaki şekilde kullanabiliriz;

AttributeTargets.Enum | AttributeTargets.Class

Conditional niteliği ise koşullu metodlar oluşturulmasını sağlar. Koşullu metod ancak #define ile tanımlandığında çağırılır. Aksi halde bu metod geçilir. Bu nitelik System.Diagnostics namespace’inin içerisinde bulunur. Aşağıdaki kodunumuza bir göz atalım;

#define SARI

using System;
using System.Diagnostics;

class Fenerbahce
{
[Conditional("SARI")]
void Sarı()
{ Console.WriteLine("Sarı Rengi"); }

[Conditional("LACİVERT")]
void Lacivert()
{ Console.WriteLine("Lacivert Rengi"); }

public static void Main()
{
Fenerbahce t = new Fenerbahce();

t.Sarı();
t.Lacivert();
}
}

Bu kodumuzun çıktısı sadece “Sarı Rengi” olur. Yukarıdaki gibi Lacivert() metodunda herhangi bir #define tanımı yapılmadığı için bu metod çalıştırılmayacaktır. Aslında bu kadar basittir bu niteliğin kullanımı. Bu nitelik sadece metodlar üzerinde uygulanabilir. Koşullu metodlarla ilgili kısıtlamalardan birisi de bu metodların her zaman void döndürmeleri gerektiğidir.

Obsolete niteliği ise bir program öğesini kullanılmayan olarak işaretlememize olanak verir. Aşağıdaki örneğimizi inceleyelim:

using System;

class Test
{
[Obsolete("Bölme2 metodunu kullan")]
static int Bolme(int x, int y)
{
return x / y;
}

static int Bolme2(int x, int y)
{
return y == 0 ? 0 : x / y;
}

public static void Main()
{
Console.WriteLine(Test.Bolme(5, 4));

Console.WriteLine(Test.Bolme2(5, 4));
}
}

Görüldüğü üzere Bolme2 metodu ile Bolme metodunun daha gelişmiş bir versiyonunu kullandık. Bu programı çalıştırdığımızda iki adet Writeline ifadesi de çalışır ve sonuçlarını 1 gösterir. Fakat kod penceresine baktığımızda Test.Bolme üzerinde aşağıdaki şekilde bir uyarı alırız:

[deprecated] int Test.Bolme(int x, int y)

Warning: ‘Test.Bolme(int x, int y)’ is obsolete: ‘Bolme2 metodunu kullan’