Tag Archives: new

C#’ta Dizi Yapısı

C#’ta bir dizi, doğrudan ulaşılabilen sayısı belli üyeleri içeren bir veri yapısıdır. Dizinin içindeki elemanlar, dizi elemanları, hepsi aynı tipte ve bu tip dizi elemanlarının tipi olarak adlandırılır.

Dizi tipleri birer referans tipidir ve dizi elemanlarının tanımlanması bu dizi örneğine referans içerir. Gerçek dizi örneği dinamik olarak runtime zamanında new operatörü kullanılarak elde edilir. New operatörü kullanılarak dinamik olarak dizilere bellek ayrılır. New operatörü otomatik olarak dizi elamanlarının default değerlerini ilk kullanıma hazırlar. Örneğin; tüm sayısal tipler için 0, tüm referans tipler için ise null.

Örneğin aşağıdaki örnek, bir int dizisi yaratıp, ilk kullanıma hazırlayıp, sonra da çıktılarını gösteren bir örnektir;

using System;

class Test 
{ 
    static void Main() 
    { 
        int[] dizi = new int[10]; 
        for (int i = 0; i < dizi.Length; i++ ) 
        { 
            dizi[i] = i * i; 
        } 
        for (int j = 0; j < dizi.Length; j++ ) 
        { 
            Console.WriteLine("dizi[{0}] = {1}", j, dizi[j]); 
        } 
    } 
}

Buradaki örnekte tek bir boyutlu dizi ile çalıştık. C# birden fazla boyutlu dizi kullanımına izin vermektedir. Bir dizinin boyut numarası aynı zamanda o dizi tipinin rank’ı olarak ta bilinir. Bir dizinin boyut sayısını dizi tanımında kullanılan dizinin içindeki virgül sayısını bir arttırarak bulabiliriz. Aşağıdaki örnekler bir, iki ve üç boyutlu dizilere örneklerdir;

int[] dizi1 = new int[10]; 
int[,] dizi2 = new int[10, 5]; 
int[, ,] dizi3 = new int[10, 5, 2];

dizi1 dizisi 10 elemana, dizi2 dizisi 50 (10 * 5) elemana ve dizi3 dizisi de 100 (10 * 5 * 2) elemana sahiptirler.

Bir dizinin eleman tipi herhangi bir tip olabilir hatta dizi tipinde bile olabilir. Dizi tipindeki elemanlara sahip dizilere Jagged array adı verilir çünkü bu dizinin elemanlarının uzunlukları aynı olmak zorunda değildir. Aşağıdaki örnek int dizi tipi içeren elementlere sahip olan bir diziyi ifade eder.

int[ ][ ] dizi = new int[3][ ]; 
dizi[0] = new int[10]; 
dizi[1] = new int[7]; 
dizi[2] = new int[20];

array-1

Yukarıdaki ilk satır, 3 elemanlı bir dizi oluşturur, bu elemanların hepsi int[] tipinde ve hepsinin varsayılan değeri null’dır. Altındaki satırlar, bu int[] tipindeki dizi elemanlarının uzunluklarını belirlerler.

Unutmayın ki new operatörü { } sınır belirleyiciler içinde dizinin elemanlarına ilk oluşturulma anında değer atamaya imkan verir.

int[ ] dizi = new int[ ] { 1, 2, 3 };

Hatta bu kullanımı aşağıdaki şekilde kısaltabiliriz de.

int[] dizi = { 1, 2, 3 };

Bu iki kullanım da aşağıdaki gösterilen adımlara eşittir:

int[ ] t = new int[3]; 
t[0] = 1; 
t[1] = 2; 
t[2] = 3; 
int[ ] dizi = t;

Dizilerde referans atama konusunda tıpkı nesneler gibi, bir diziyi diğer diziye atadığınızda sadece değişkenin ilişkili olduğu nesneyi değiştiriyorsunuz. Örneğin aşağıdaki programda, dizi tanımlamaları yapılan dizi1 ve dizi2 dizileri, atama yapıldıktan sonra her iki dizi referans değişkenleri de aynı nesneyi gösterdiğinden dizi değerleri aynı olur.

using System;

class Test 
{ 
    static void Main() 
    { 
        int[] dizi1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
        int[] dizi2 = { 0, -1, -2, -3, -4, -5, -6, -7, -8, -9 };

        dizi2 = dizi1;

        foreach(var i in dizi1) 
        { 
            Console.Write(dizi1[i]); 
        }

        Console.WriteLine();

        foreach (var i in dizi2) 
        { 
            Console.Write(dizi2[i]); 
        } 
    } 
}

Çıktı:

0123456789

0123456789 Press any key to continue . . .

C#’ta Struct Yapısı

Sınıflar gibi, struct yapıları veri ve fonksiyon üyeleri içeren veri yapılarıdır. Sınıflardan farklı olarak, struct yapısı değer tipidir ve heap bölge tahsisi gerektirmez. Bir struct değişkeni, direkt olarak struct’ın verisini tutar. Oysa sınıf tipinde bir değişken, dinamik olarak ayrılmış nesneye referans tutar. Struct yapısı, kullanıcı tanımlı kalıtımı desteklemez ve tüm struct yapıları dolaylı olarak object tipinden kalıtım alır.

Önemli: Struct yapılarının heap bölge tahisisi gerektirmemesi, bu yapıların hiçbir zaman bunu yapmadıkları anlamına gelmez.

Bu yapılar özellikle küçük veri yapıları kullanımında kullanışlı olurlar. Karmaşık sayılar ve koordinat sisteminde noktalar struct yapılarına iyi örneklerdir. Bir yapıda sınıf yerine struct kullanımı, bellek tahsis etmede ve programınızın performansında büyük farklılıklar yaratabilir.

Örneğin; aşağıdaki program 100 nokta oluşturan bir programdır. Sınıf yapısı ile kullandığımızda 101 adet farklı nesne – 1 adet dizi için, 100 adet te dizi üyeleri için – oluşturulur:

using System;


class Nokta

{

    public int x, y;


    public Nokta(int x, int y)

    {

        this.x = x;

        this.y = y;

    }

}

class Test

{

    static void Main()

    {

        Nokta[] noktalar = new Nokta[100];

        for (int i = 0; i < 100; i++)

            noktalar[i] = new Nokta(i, i);

    }

}

struct-1

Alternatif olarak Nokta yapısını struct yapabiliriz.

struct Nokta

{

    public int x, y;


    public Nokta(int x, int y)

    {

        this.x = x;

        this.y = y;

    }

}

Burada sadece 1 nesne yaratılır – 1 adet dizi için – ve Nokta örnekleri dizi içerisinde saklanırlar.

struct-2

Önemli: Struct yapısının performans açısından sağladığı faydayı “Her zaman struct kullanın” şeklinde algılamak yanlış olur. Tabi ki bazı senaryolarda struct yapısına bellek ayırmak ve bellekten almak daha az zaman alır fakat her struct ataması bilindiği gibi değer kopyalamasıdır (value copy). Bu her zaman referans kopyalamasından daha fazla zaman alır.

Struct yapıcıları new operatörü ile çağrılırlar fakat bu demek değildir ki belirli bir bellek ayrılmıştır. Nesnenin veya referansının dinamik olarak bellek ayrımı yerine, bir struct yapıcısı basitçe struct değerinin kendisini döndürür (genellikle yığının geçici bölgesinde) ve bu değer gereli olduğunda kopyalanır.

Sınıflarda, iki farklı değişken aynı nesneye referans gösterebilir ve bu şekilde bir değer üzerindeki işlemler aynı nesneyi referans eden diğer değişkeni etkileyebilir. Struct yapılarında, her değişken kendi verisinin kopyasını tutar ve bir değişkenin diğerini etkilemesi imkansızdır. Örneğin aşağıdaki kodu inceleyelim;

Nokta a = new Nokta(10, 10);

Nokta b = a;

a.x = 20;

Console.WriteLine(b.x);

Eğer buradaki Nokta örneği bir sınıf (class) ise, sonucumuz 20 olur çünkü a ve b aynı nesneye referans gösterirler. Eğer Nokta örneği bir struct ise, sonucumuz 10 olur çünkü ikinci satırdaki atama işlemi değer olarak a’daki değerleri b’ye kopyalar ve bu kopyalama 3. satırdaki a.x atamasını etkilemez.

Bu örnekte struct kullanımının 2 noktasının farkına vardır. Birincisi, tüm struct yapısını kopyalamak bir nesne referansını kopyalamaktan daha az verimli. Böylece atama yapma ve değer parametresi geçirme struct yapılarında referans tiplerine göre daha maliyetli olabilir. İkincisi, ref ve out parametreleri dışında, struct yapısına referans oluşturma imkansızdır.

Bill Wagner: Eğer tüm durumlarda değer anlamı (value semantics) istemiyorsanız, sınıf kullanmalısınız. Sınıflar bazı durumlarda değer anlamı uygulayabilirler, (string gibi) ama default olarak referans anlamına uyarlar. Bu fark sizin tasarımınızda yığın vs hesap bellek ayrımına göre daha çok farklılık gösterir.