28 Şubat 2011 Pazartesi

C# Static Olmak, Non-Static Olmak

"Static" sıkça karşılaştığımız bir anahtar sözcüktür(keyword). C#'da kullandığımız bütün sınıflar(class), ve üyeleri(metot, field, property, delegate, event, constructor) için olmassa olmaz bir kavramdır. Bir sınıf veya üye tanımlanırken static anahtar sözcüğü yazılırsa, o sınıfa veya üyeye, static'tir deriz. Eğer yazılmamışsa non-static'tir deriz. (Burada constant; -diğer bir adı ile değişmezler veya sabitler- istisnai bir duruma sahiptir. Constant üyelere static anahtar sözcüğünu yazamayız. Çünkü default olarak static tanımlanırlar. Tanımlarken static yazmaya zorlarsak derleme zamanı hatası alırız.)

Bir Sınıfın Static Olması

Bir sınıf static olarak tanımlanıyosa, içerisindeki bütün üyeler static olarak tanımlanmak zorundadır. Amaç tüm üyeleri static olmaya zorlamaktır. Static sınıfların özellikleri aşağıdaki gibidir:

-C# 2.0 ile birlikte static anahtar sözcüğü class'lara da uygulanabilir bir hale geldi..
-Bir class'a static anahtar sözcüğü uygulandığından içindeki tüm üyeleri static olmaya zorlamış oluruz...
-Tek bir özel nesne örneği, sınıfın kullanıldığı anda ram'in Static bölgesine çıkar...
-Static sınıflar new ile örneklenemezler. Dolayısıyla heap'te nesne örnekleri bulunmaz.

Üyelerin Static Olması

C#’da bir sınıfa ait üyeler; metotlar, alanlar(field), özellikler(property), olaylar(event), delegeler(delegate), constructor(yapıcı) metot olarak sıralanabilir. Bu makalede metotlar ve alanlar üzerinde duracağız. Bir üyenin static olması demek; o üyeye erişimin, SınıfAdi.UyeAdi olarak yapılabilmesi anlamına gelmektedir. Bir üyenin non-static olması demek; o üyeye ancak ve ancak, tanımlandığı sınıfa ait bir nesne örneği üzerinden erişebilmemiz anlamına gelir.

ÖNEMLİ NOT: Static, bir erişim belirleyici değildir!

Bir üye neden static olmalıdır? Aynı şekilde, bir üye neden non-static olmalıdır? Şimdi bu sorulara örnek seneryolar aracılığı ile cevap bulmaya çalışalım.

-Static Field(Alan)

Öncelikle, yeni bir console uygulaması açalım. Daha sonra uygulamaya, adı “Urun” olan yeni bir sınıf ekleyelim. Bu sıfın içerisinde de Id, Ad ve Fiyat bilgilerini tutalım. Seneryomuza göre, oluşan ürün nesneleri için kdv bilgisini de tutmak istiyoruz. Dolayısıyla oluşturduğumuz sınıfa kdvOran alanını da ekleyelim. Benim oluşturduğum Urun sınıfının kodları aşağıdaki gibidir;

class Urun

        {

            public int Id;

            public string Ad;

            public double Fiyat;

            public double KdvOran;

        }

Yukaridaki kodlara baktığımızda, alanların non-static olduklarını söyleyebiliriz. Yazının girişinde bahsettiğim gibi, eğer static anahtar sözcüğü yazılmamışsa, o üye non-static demektir. Üyenin non-static olması da; üyeye erişimin, ancak Urun tipinden bir nesne örneği üzerinden yapılabileceği anlamına gelmektedir. Şimdi 2 tane Urun nesnesi oluşturarak bu alanlara değer atayalım;

 

class Program

        {

            static void Main(string[] args)

            {

                Urun urun1 = new Urun();

                urun1.Id = 1;

                urun1.Ad = "Monitör";

                urun1.Fiyat = 200;

                urun1.KdvOran = 0.18;

 

                Urun urun2 = new Urun();

                urun2.Id = 2;

                urun2.Ad = "Klavye";

                urun2.Fiyat = 80;

                urun2.KdvOran = 0.18;

            }

        }

 

Oluşturduğumuz urun1 ve urun2 nesnesinin RAM(Bellek) üzerindeki görüntüsü sembolik olarak şu şekildedir;

 

resim3

Dikkat ederseniz Id, Ad ve Fiyat bilgisi her nesne örneği için özel bir değerdir. Yani urun1 nesnesini, urun1 yapan özelliklerdir. urun2 nesnesi için de aynı şeyi söylememiz mümkündür. Ancak KdvOran alanında göze çarpan önemli bir nokta var. Bu alanın değeri her nesne örneği için özel bir değer değildir, kdv değeri genel bir değerdir ve tüm Urun nesneleri için ortaktır. Dolayısı ile yukarıdaki kod bloğunu göz önünde bulundurursak, Kdv bilgisinin her Urun nesnesi için tekrar tekrar tanımlandığını ve tekrar tekrar değerinin atandığını görürüz. Bu örnekte elimizde 2 adet Urun nesnesi olduğundan, bu bir problem olarak algılanmayabilir. Ancak elimizde 100000 adet Urun nesnesi olsaydı, 100000 kere aynı işi tekrarlamış olacaktık ve kötü bir programlamcılık yapmış olacaktık. Kodlarımızı yazarken bu tarz detaylara önem vermemiz; hem performans, hem de kod okunurluğu açısından önemli bir avantaj sağlar.

Şimdi gelelim doğru tasarımı yapmaya. Urun sınıfı tasarımımızı aşağıdaki gibi değiştirelim;

class Urun

        {

            public int Id;

            public string Ad;

            public double Fiyat;

            public static double KdvOran;

        }

KdvOran alanının tüm Urun nesneleri için ortak olduğu konusunda hem fikir olduğumuzu düşünüyorum. İşte tam bu noktada “static” anahtar sözcüğü imdadımıza yetişecektir. KdvOran alanını static olarak tanımladığımız takdirde; artık her nesne örneği için özel bir alan olmaktan çıkıp, o sınıfa ait genel bir alan haline gelecektir. Yani her nesne örneği için tekrar tekrar kdv alanı oluşturup değer atamak yerine, static bir alan olan KdvOran’a bir kere değer ataması yapmamız yeterli olacaktır. Artık istediğimiz yerde Urun.KdvOran (SınıfAdı.UyeAdı) diyerek bu alanı kullanabiliriz.

Yukarıdaki sınıf tasarımından sonra nesne örneklememizde artık urun1.KdvOran veya urun2.KdvOran diyemeyeceğiz. Çünkü Urun tipinden olan nesne örnekleri üzerinden, sadece o sınıfa ait non-static üyelere ulaşabiliriz. Nesne örneklerken kullanacağımız kodlar ;

class Program

        {

            static void Main(string[] args)

            {

                Urun urun1 = new Urun();

                urun1.Id = 1;

                urun1.Ad = "Monitör";

                urun1.Fiyat = 200;

 

                Urun urun2 = new Urun();

                urun2.Id = 2;

                urun2.Ad = "Klavye";

                urun2.Fiyat = 80;

 

                Urun.KdvOran = 0.18;

            }

        }

Yaptığımız son sınıf tasarımına göre, nesnelerin RAM üzerindeki sembolik gösterimi şu şekilde olur ;

 

resim6

Bu seneryodan yola çıkarak şu tanımı yapmamız mümkündür:

-Bir alan(field); o sınıfa ait her bir nesne örneği için özel bir alan ise, non-static olarak tanımlanmalıdır. Eğer her nesne örneği için ortak bir alan ise static olarak tanımlanmalıdır.


-Static Metot

Şimdi de metotların static veya non-static olma durumlarını inceleyelim. Yukarıda tasarladığımız Urun sınıfı üzerinden devam edelim.

Urun sınıfımıza ZamYap adlı bir metot eklemek istiyoruz. Metot geriye değer dönmesin ve parametre olarak yapılacak zam oranını alsın. Bu noktada düşünmemiz gereken ilk nokta şu olmalı, static mi non-static mi? Aslında bu metot hem static hem de non-static olarak yazılabilir. Ancak hangisinin daha doğru olduğuna, ikisini de deneyerek karar verelim.

İlk olarak Urun sınıfı içine static olarak ZamYap metodunu tanımlayalım. Metodu Urun sınıfının içine yazmamızın sebebi, yapılan işin Urun nesnelerinin işleri olmasıdır. Yani bu metodu Program sınıfının içine yazmak anlamsız olacaktır. Urun sınıfının içindeki ZamYap metodunun kodları aşağıdaki gibidir;

public static void ZamYap(Urun u, double zamOran)

{

    u.Fiyat += u.Fiyat * zamOran;

}

Dikkat ederseniz, metoda Urun tipinden bir parametre ilave edilmiş durumda. Bunun nedeni gayet basit; metot static olduğundan dolayı, erişim Urun.ZamYap şeklinde yapılmalıdır. Peki bu metot hangi ürün nesnesi için çalışığını nereden bilecek? İşte bu yüzden Urun tipinden bir parametre vermek zorundayız ki, hangi ürünün fiyatına zam yapacağını belirtebilelim.

Program tarafında metodun çağırılması da şu şekilde olur;

class Program

        {

            static void Main(string[] args)

            {

                Urun urun1 = new Urun();

                urun1.Id = 1;

                urun1.Ad = "Monitör";

                urun1.Fiyat = 200;

 

                Urun.ZamYap(urun1, 0.20);

                Console.WriteLine(urun1.Fiyat);

            }

        }

Yukarıdaki kod çalıştırıldığında urun1’in fiyatının 240 olduğunu görürüz.

Peki bu doğru bir kodlama oldu mu dersiniz? İşte bu noktada düşünmemiz gereken en önemli nokta şu olmalı; bu metot her Urun nesnesi için özel bir iş mi yapmaktadır, yoksa genel bir iş mi yapmaktadır? Cevap ortadadır, bu metot her ürün nesnesi için özel bir iş yapmaktadır. Yani hangi ürün için çağırılırsa, o ürün için özel olan “Fiyat” alanını kullanarak o ürünün fiyatına zam yapmaktadır. İşte bu yüzden yukarıdaki kodlama, istediğimiz sonucu almamıza rağmen doğru bir kodlama değildir. Bu metot her nesne için özel bir iş yaptığından dolayı non-static olarak tanımlanmalıdır.

Doğru tasarım şu şekilde olmalıdır;

public void ZamYap(double ZamOran)

{

    Fiyat += Fiyat*ZamOran;

}

Dikkat ederseniz static olarak tanımlanan metotta olan Urun parametresine artık gerek kalmadı. Çünkü metot non-static olduğundan dolayı, bir Urun sınıfı nesnesi üzerinden çağırılmak zorundadır. Bir diğer çok önemli nokta; metodun içerisinde, direkt “Fiyat” diyerek eriştiğim alan, metot hangi nesne için iş yapıyorsa o nesnenin “Fiyat” alanıdır. Metoda urun1.ZamYap diyerek erişeceğimden dolayı, metodun içerisindeki "Fiyat" alanı, urun1'in fiyat alanıdır. Buradan şöyle bir sonuca varmak mümkündür; bir sınıf içerisindeki non-static metotlar, o sınıfa ait non-static veya static alanları direkt olarak kullanabilirler. Ancak bir sınıf içerisindeki static metotlar , yalnızca o sınıfa ait static alanları kullanabilirler.

Şimdi program tarafında metodun çağırılmasına bakalım;

class Program

        {

            static void Main(string[] args)

            {

                Urun urun1 = new Urun();

                urun1.Id = 1;

                urun1.Ad = "Monitör";

                urun1.Fiyat = 200;

 

                urun1.ZamYap(0.20);

                Console.WriteLine(urun1.Fiyat);

            }

        }

Metodun bu haliyle, daha doğru bir kodlama yaptığımızı görmek mümkün. artık her Urun nesnesi için özel bir iş yapan ZamYap metodu, hangi nesne için çağırılıyorsa o nesne üzerinden çalışmaktadır.

Şimdi de seneryomuza KdvOranDegisti adında bir metot tanımlayalım. Aklımıza ilk gelen şey, bu metodun her nesne için özel bir iş yapıp yapmaması sorusudur. Bu metodun amacı, static bir alan olan, yani Urun sınıfı için genel bir alan olan KdvOran değerini değiştirmektir. Dolayısı ile bu işin oluşan nesneler ile bir alakası yoktur. Bu sebepten ötürü metodu static olarak tanımlamalıyız;

public static void KdvOranDegisti(double YeniKdvOran)

{

    KdvOran = YeniKdvOran;

}

Metodu static olarak tanımladığımız için Urun.KdvOranDegisti(….) diyerek metoda erişim sağlayabiliriz. Yani herhangi bir Urun sınıfı nesne örneğine ihtiyaç duymayız.

Eğer bu metodu non-static olarak tasarlamış olsaydık (Yanlış tasarım);

public void KdvOranDegisti(double YeniKdvOran)

{

    KdvOran = YeniKdvOran;

}

Yukarıda yazılan metoda erişebilmemiz için bir Urun nesnesi üzerinden gitmemiz gerekecekti;

urun1.KdvOranDegisti(0.08);

urun2.KdvOranDegisti(0.18);

Buradan da anlaşılacağı gibi yapılan işin urun1 veya urun2 nesnesi ile hiçbir alakası yoktur. dolayısı ile bu metodun non-static olarak tanımlanması doğru bir tasarım değildir.

Sonuç => Bir sınıfa ait her nesne için özel bir iş söz konusu ise, metot non-static tanımlanmalıdır, Eğer yapılan iş genel bir iş ise, metot static olarak tanımlanmalıdır.



35 yorum:

Ali ÖZDEMİR dedi ki...

anlatım 10 numara

Onur Salkaya dedi ki...

Teşekkür ederim, faydalı olmasına sevindim.

Mustafa HOCA dedi ki...

Onur Bey gerçekten anlatım çok güzel omuş..Bazı anlatımlarla karşılaşıyoruz; Sadece kişi o konu hakkında bilgili olduğunun show'unu yapmakla yetiniyor..Öğrenmek isteyenlere bişey verdiği yok..Lakin sizin anlatımınız gerçekten bişeyler öğretmek amaçlı olup çok faydalı olmuş..BU satırlar daha böyle uzayıp gider...Fakat sözü fazla uzatmadan tekrardan teşekkürlerimi sunuyorum.BU tatmin edici anlatımınız için..

Onur Salkaya dedi ki...

Değerli yorumunuz için çok teşekkür ediyorum Mustafa Bey. İşinize yaraması beni mutlu etti.

Adsız dedi ki...

bende tatmin oldum :/ teşekkürler onur bey

Adsız dedi ki...

Onur Bey, Yalın aydınlatıcı anlatımlarınızdan dolayı sizi tebrik ederim. Kafamda soru işaretleri olan, tam anlaşılmamış pek çok konu üzerine makale yazmışsınız. Çok teşekkürler... Makaleleriniz takipçisiyim.

Onur Salkaya dedi ki...

Çok teşekkürler, kolay gelsin.

Adsız dedi ki...

Sitenizdeki paylaşımlar çok doyurucu..Şunu Sormak istiyorum tarihe baglı olarak sırayla makalelerinizi okumamı tavsiye edermisiniz..Yoksa kendi içinde bir düzene sahip değillerse farklı bir yol haritası çizeyim kendime..Bende Obje mantığına yeni adapte olan bir yazılımcı adayıyım..Paylaşimlar için yeniden teşekkür ederim

Onur Salkaya dedi ki...

Merhaba,

Öncelikle değerli yorumunuz için çok teşekkür ederim. Tarih sırası ile gitmenizi önerebilirim. Şubat ayındaki sıralama biraz karışık olmuş. Normal şartlarda sıra şu şekilde olmalıydı.

- .Net Mimarisi ve Çalışma Yapısı
- İle İlk Uygulama
- Constructor (Yapıcı) Metot
- Static Olmak, Non-Static Olmak
- Encapsulation Kavramı
- Encapsulation ve Property Kavramı

Şubat ayı için yukarıdaki sırayı takip edip, diğer aylar içerisinde yer alan yazıları da normal sırası ile okumanızı tavsiye ederim. İsteğe bağlı olarak SQL kısımlarını atlayabilir veya yüzeysel geçebilirsiniz.

İyi çalışmalar

Adsız dedi ki...

İlginiz için teşekkür ederim.Takipteyim.

Adsız dedi ki...

emeğinize sağlık çok iyi bir anlatım olmuş.konuyu çok iyi anladım.

Alpaslan dedi ki...

Tebrik ederim muhteşem bir anlatım, makalelerinizin devamını bekliyorum.

Adsız dedi ki...

Gayet başarılı!

su dedi ki...

Tebrikler bende bir yazılım eğitimcisi olarak örnekleri çok yerinde buldum. Çalışmalarınızın devamını dilerim.

Onur Salkaya dedi ki...

Teşekkür ediyorum, iyi çalışmalar diliyorum.

Adsız dedi ki...

Tebrik ederim çok güzel ve aydınlatıcı bir yazı olmuş..

Unknown dedi ki...

cok guzel bir anlatim, tesekkurler

Cüneyt Çarıkçi dedi ki...

Teşekkürler :)

erata dedi ki...

basit ve güzel anlatım için teşekkürler.
yeni yeni bazı kısımlar tam oturmaya başladı kafamda sayenizde.

Adsız dedi ki...

anlatım çok başarılı teşekkürler

Adsız dedi ki...

Açıklayıcı ve öğretici anlatımınız için teşekkürler hocam...

Adsız dedi ki...

Danke. Gercekten guzel bir örnek.

Adsız dedi ki...

Teşekkürler, Faydalı ve güzel bir bilgilendirme olmuş.

Adsız dedi ki...

Sadece yazmak için yazılmayan, gerçekten okuyana bir şeyler katmak için yazılan harkulade,şahane bir makale...Çok makaleler okudum hiç yorum yapmadım şu ana kadar taki sizin yazınızı okuyana kadar.
Bu kadar aydınlatıcı, insana değer katan yazınızı okuduktan sonra yorum yapmamak, yapılan bu güzel emeğe büyük haksızlık olacağını düşündüm. Elinize sağlık...

Adsız dedi ki...

Eyvallah ustad.

Adsız dedi ki...

Bir konu bu kadar mı güzel anlatılır ya, elinize emeğinize sağlık. Çok saolun...

Adsız dedi ki...

anlatım sade ve şık. anlatılanlar bilgilerimizi tazeleyici olmuş. teşekkürü bir borç bilirim.

Unknown dedi ki...

Faydalı bir paylaşım, emeğiniz için teşekkürler.

Adsız dedi ki...

Çok güzel sağolun

jcb913 dedi ki...

allah razı olsun hocam. müthiş anlatım. yazdığın kod hata görmesin :)

Ferhat Yıldırım dedi ki...

Teşekkür ederim, doyurucu anlatım

Unknown dedi ki...

Yazılarını ve anlatım şeklini çok sevdim verilmek istenen neyse onu veriyorsun. Bu güzel yazılar için teşekkürler :)

Gökçe Dengiz dedi ki...

Çok açıklayıcı ve anlaşılır...teşekkürler

Unknown dedi ki...

Yalın ve anlaşılır anlatım için teşekkürler.Anlamak için en 5 sayfa gezdim sizinki kafi geldi

Secret dedi ki...

İlk kez neden static kavramının kullanıldığını tam olarak anladım

Yorum Gönder