C# Classes (Sınıflar)

Merhaba arkadaşlar,

Bu yazımda C# dilinde Sınıflar (Class) hakkında bahsedeceğim. Aslında bu konu nesne yönelimli programlama için bir yapı taşıdır diyebiliriz. Zaten C#’ta her şey obje mantığı vardır. Eğer nesne yönelimli programlama dendiğinde aklınıza bir şey gelmiyor ise korkmayın 🙂 sorun yok bu konuyu daha sonra ele alacağız.

Nesne yönelimli programlama dilini kısaca özetleyecek olursak; böl ve yönet stratejisi gibidir. Yani elimizde çözülmesi gereken büyük bir parça var ve bu parçayı tek seferde çözmek bizim için zor ( imkansız değil tabi ) olacak . O yüzden bütünü parçalara böleriz. Her bir parça kendi özelliğinden sorumludur. Böldüğümüz bu parçalara nesne denir. Her bir parçanın kendi görevini yapmasını sağlarsak ve böldüğümüz parçaları da kendi aralarında ilişkilendirerek büyük çaplı programlar geliştirme tekniğine de Nesne Yönelimli Programlama ( OOP ) denir.

An introduction to Object-Oriented Programming in JavaScript

Nesne yönelimli programlama dersini aldığımda programlamaya bakış açım değişmişti. O zamanlar üniversitede java programlama dilini kullanıyorduk ve bir sınıfın diğer sınıflardan extend edilmesi veya interface’lerden implemente edilerek o sınıfların özelliklerini kazanması inanılmaz güzel bir bilgi gelmişti bana. Hatırlıyorum da nasıl oluyor bu falan olmuştum. Nesne yönelimli programla tekniğini kullanarak yaptığımız ilk uygulamamız çok klasiktir ama hesap makinesiydi 🙂 Fazla sözü uzatmaya gerek yok Nesne Yönelimli Programlamanın temel yapı taşlarından biri olan sınıf ile işe koyulalım 🙂

Her yazımda olduğu gibi bu yazıya ilham veren motivasyon cümlemiz:

Sen dünyasın; o yüzden sen değişirsen dünya değişir.

Osho

Bir önceki yazımda Metotlar ve Fonksiyonlar’ı ele almıştık. Hatırlamak amacıyla konu başlıkları nelerdi anımsayalım. Yazıya buradan ulaşabilirsiniz.

  • Değer ve Referans Parametreleri
  • Ref Olarak İşaretlenen Geri Dönüş Değerleri
  • Metotların Aşırı Yüklenmesi ve İmza Kavramı
  • Özyineli (Recursive Metotlar)
  • Yerel Metotlar (Local Functions)

Sınıflar

Sınıflar nesne yönelimli programlama tekniğini destekleyen bütün programlama dilleri için temel bir yapı taşıdır. Sınıflar programcıya aslında veri modeli sunar. Bu veri modelleri kullanılarak çeşitli nesneler oluşturulur. Aslında bundan önceki yazılarda sınıfları çok kullandık. Örneğin diziler konusunda System.Array sınıfını kullandık. Bu sınıfın nimetlerinden yararlandık. Dizinin uzunluğu (Length), sıralanması (Sort), dizinin kopyalanması (Copy) gibi gibi.

  • Sınıf bildirimleri class anahtar sözcüğü ile yapılır.
  • Sınıfların üye elamanları değişkenler (özellik) ya da metotlardır.
  • Değişkenler ve metotların erişim belirleyicileri (access modifiers) vardır

Sınıflara örnek bir kod:

Note: Sınıf bildirimleri için bellekte yer tahsil edilmez. Sınıf bildirimleri sonradan tanımlayacağımız nesneler için derleyiciye nesnenin neye benzediğini gösterir.

Erişim Belirleyicileri (Access Specifiers\Modifiers)

Her metot ve özellik için bildirim yapmadan önce erişim belirleyici türünü bildirmemiz gerekiyor. Yani erişim belirleyiciler bir metoda veya özelliğe nerelerden erişileceğini tanımlar.

C# dilinde altı tane erişim belirleyicileri vardır. Bunlar public, private, protected, internal, protected internal, private protected.

Örneğin hatırlarsanız System.Array sınıfına ait birden çok erişim belirleyicileri kullanılmıştır. Array sınıfının Sort methodu public erişim belirleyicisi ile bildirilmiştir ve bu sebeple Syste.Array sınıfından bu metoda erişebiliyoruz. Public üye elamanları genellikle bir sınıfın dışarıya sunduğu bir arayüzü ifade eder. Ancak bazı durumlarda private olarak bildirilmiş üye elamanları da arayüzde dahil edilebilir. private tanımlanan üye elamanları gerçekten bir takım değerleri tutmak ve işe yarayan metotları kullanabilmek için tanımlanır.

Erişim belirleyicilerini tam anlamak için örnek üzerinden gidelim.

Public erişim belirleyicisi sınıfın bir özelliğine veya metoduna aynı assembly’de yada referans edilmiş farklı bir assembly’lerden erişilebilir.

Private erişim belirleyicisi sınıfın bir özelliğine veya metoduna sadece aynı sınıf yada yapı (struct) üzerinden erişilebilir.

Protected erişim belirleyicisi sınıfın bir özelliğine veya metoduna yalnızca aynı sınıftan veya bu sınıftan türetilen başka bir sınıftan erişilebilir.

Internal erişim belirleyicisi sınıfın bir özelliğine veya metoduna aynı assembly’den erişilebilir ancak farklı bir assembly’den erişilemez.

Protected Internal erişim belirleyicisi sınıfın bir özelliğine veya metoduna bildirilen assemly’de herhangi bir yerden veya farklı assemly’lerde bu sınıftan türetilen bir sınıftan erişilebilir. Yani bu özellik bitsel operasyondaki OR (VEYA) gibi düşünebilirsiniz. Protected erişim özelliliği ile birlikte Internal erişim özelliğini kapsamaktadır.

Private Protected erişim belirleyicisi sınıfın bir özelliğine veya metoduna bildirilen assemly içindeki aynı sınıftaki kod’tan veya bu sınıftan türetilen başka bir sınıftan erişilebilir. Yani bu özellik bitsel operasyondaki OR (VEYA) gibi düşünebilirsiniz. Private erişim özelliliği ile birlikte Protected erişim özelliğini kapsamaktadır.

Erişim belirleyicileri örnek kodun son hali

Önemli: “Herhangi bir erişim belirleyicisi ile işaretlenmemiş elamanlar, private olarak kabul edilir.”

Birden fazla sınıf nesnesi tanımlama

Kodlamaya başladığımız programımız içerisinde bir nesneden birden fazla üretmek isteyebiliriz. Bu noktada ürettiğimiz bu nesneleri birbirine atarken dikkat etmeliyiz. Daha önce serilerde bahsettiğim Değer ve Referans tipler arasındaki temel farkı anımsayalım. Değer tipler birbirine atanırken bitsel kopyalanıyordu ve atama işlemi bittikten sonra her bir nesnenin farklı bellek bölgesinde verileri temsil ediyordu. Ancak hatırlarsanız referans tiplerde bu durum farklıydı. İki nesneyi birbirlerine atadığımızda nesnelerin eleman değerleri kopyalanamaz dinamik bellek bölgesinde referansları tutulur. Yani eğer iki nesneyi birbirine eşitlersek iki nesnede aynı bellek bölgesindeki verileri temsil eder. Bir nesne üzerinden yapılan değişiklik diğerini de etkiler. (Değer ve referans tipleri tekrar hatırlamak için buradan erişebilirsiniz)

Yukarıdaki örnekte brain sınıfı için bellekte yer tahsil edilmesine rağmen brain1 için yer tahsil edilmemiştir. brain 1 için new anahtar sözcüğü kullanılmadı için sadece stack bölgesinde bir referans tutabilecek kadar yer ayrılmıştır. brain1 = brain ifadesiyle brain1 referans adresi brain in referans adresi ile aynı olmaktadır. Bu yüzden birinde oluşabilecek değişiklik her ikisini de etkiler.

Referans tiplerinin birbirlerine atanması ile ilgili bütün anlatılanların özetini aşağıdaki şemada görebilirsiniz.

this Anahtar Sözcüğü

Nesneler üzerinden metotlar çağrılırken aslında biz farkına varmadan metotlara gizlice ilgili nesnelerin referansları geçirilir. Örneğin, this anahtar sözcüğünün anlamını daha iyi kavrayabilmek için bir soyutlama yapalım;

Eğer this anahtar sözcüğü olmasaydı ikinci şekildeki gibi kullanmaya mecbur kalacaktık. Ve bu çok kullanışsız kullanım olurdu. Bu yüzden C# tasarlayanlar brain referansını gizlice Speak() metoduna aktarmada kullanılacak bir yapı geliştirmişler. İşte this anahtar sözcüğü bu referansı temsil etmektedir. this anahtar sözcüğü çok kullanılmasa da bazı durumlarda faydalı olabilir. Örneğin;

Brain sınıfının Weight üyesini SetWeight metodu ile değiştirdiğimizde ilk kod bloğunda SetWeight metodu yüzünden sınıfın Weight parametresi görünmez hale gelmiştir. İlk kod parçasını derlediğinizde int değerinin default değeri olan 0 değerinin Weight parametresine atacağını görürsünüz. Bu durumda this anahtar sözcüğünden faydalanılarak nesnenin referansı olduğundan mantıksal bir hatayı önlemiş oluruz.

Özelliklerde Erişim Belirleyiciler

Aynen metotlarda olduğu gibi özellikler (property) ile birlikte erişim belirleyicileri kullanılabilir. C# 2.0 ile birlikte gelen yeniliklerden biri de get ve set bloklarının farklı erişim belirleyicileri ile belirleye bilmemizdir.

Özelliklerin erişim belirleyicileri hakkında bilinmesi gereken en önemli nokta, özellik bildiriminde bildirilen erişim belirleyicisinin get veya set bloğunda tanımlanan erişim belirleyicisinden daha yüksek erişim yetkisine sahip olması gerektiğidir. Örneğin özellik private olarak bildirilmişse get veya set blokları public olamaz. Aynı şekilde özellik public olarak bildirilmişse get ve set blokları yine public olamaz. Kısaca özellik public ise get ve set blokları private, protected, internal yada protected internal olabilir.

Yapıcı Metotlar (Constructors)

Bir nesnenin dinamik olarak yaratıldığı anda otomatik olarak çalıştırılan metotlar vardır. Bunlar sayesinde nesnenin üye elamanlarına ilk değerler verilir yada sınıf nesnesi için gerekli kaynak düzenlemeleri yapılır. (Örneğin DB’ye bağlanan bir sınıf ise bağlantı oluşturması gibi)

  • Yapıcı metotlar biz tanımlasak da tanımlamasak da kesin bir yapıcı metodu vardır. Buna default constructor denir.
  • Varsayılan yapıcı metotlar (default constructor) herhangi bir değer almazlar.
  • Yapıcı metotların geri dönüş değeri yoktur.
  • Yapıcı metotların ismi sınıf ile aynı olmak zorundadır.
  • Yapıcı metotlar sınıfın bütün elamanlarına varsayılan değeri atar.

Time isimli bir sınıf ve bu sınıfın yapıcı metodunu kullanarak for döngüsü ile o anki zamanı yazdıralım.

Not: Yapıcı metotlar dışarıdan çağrıldığı için public erişim belirleyicisi ile tanımlamalıyız.

Varsayılan Yapıcı Metot (Default Constructor)

  • Her sınıf nesnesinin en az bir yapıcı metodu vardır.
  • Varsayılan yapıcı metodun herhangi bir parametresi yoktur.
  • Varsayılan bu metot nesnelerin üye elamanlarına varsayılan değerler atar. (ör: int:0, bool:false)
  • Yapıcı metotlara aşırı yüklenebilir.
  • this anahtar sözcüğü ile diğer yapıcı metotları oluşturulabilir.

Yapıcı Metotlara örnek için DefaultTime isminde sınıf oluşturup yapıcı metotlarını aşırı yükleyelim

Kopyalayıcı Yapıcı Metot (Copy Constructor)

  • Kopyalayıcı yapıcı metotların tek parametresi vardır.
  • Bu parametreler sınıf türünden nesnelerdir.

Not: C++ dilinde bir sınıf nesnesi başka bir sınıf nesnesine aktarılırken özel bir kopyalayıcı metodu çağrılmaktadır. Ancak C#’ta iki sınıf nesnesi birbirlerine aktarılırken sadece referanslar aktarılır.

Kopyalayıcı yapı metot için CopyTime isminde sınıf oluşturup yapıcı metodunu aşırı yükleyerek metoda sınıf parametresini verelim

Yıkıcı Metotlar (Destructors) ve Dispose() Metodu

Nesnelere erişimin mümkün olmadığı yerlerde nesnelerin artık bellekte kalmasının bir anlamı yoktur. C++ dilinde bellekten tahsil edilen alanlar geri iade edilmesi gereklidir, zira heap bölgesinden tahsis edilen alanlar geri iade edilmezse bellek sızıntılar (memory leak) olabilir. C# programcıların bu zahmeti çekmesine gerek yoktur. Gereksiz Nesne Toplama (Garbage Collection) dediğimiz mekanizma gereksiz nesnelerin tuttuğu referans bölgelerin zamanı geldiğinde geri iade eder. Garbage Collector bizim yerimize nesnelerin referanslarını geri iade eder ama nenelerin faaliyet alanlarının bittiği noktada olacağının garantisini vermez. Bu yüzden C# tanımlanan Yıkıcı Metotlar (Destructor) bu iade işleminden hemen önce çalışacağı kesindir.

C# dilinde bir nesnenin kaynakları iki şekilde iade edilir. Birincisi Dispose() metodu kullanmak diğer ise Yıkıcı Metotlar (Destructor) bildirmektir.

Not: Gereksiz nesne toplayıcısı (garbage collector) bizim isteğimiz dışında çalışmaktadır. Ancak .Net sınıf kütüphanesindeki bazı sınıflar (System.GC) yardımı ile istediğimiz anda GC devreye sokabiliriz. Bu durum pek tavsiye edilmez!

Yıkıcı Metotlara örnek: GamePlayerService nesnesi oluştuktan sonra nesnenin ihtiyaç olmadığı anda Yıkıcı Metot çalışacaktır.

Not: Bir sınıfın sadece bir yıkıcı metodu olabilir.

Statik Üye Elemanları

Statik elemanlar bir sınıfın global düzeydeki elemanlarıdır. Yani statik elemanları kullanmak için herhangi bir nesne yaratmamıza gerek yoktur.

C# kütüphanesinde Math sınıfı statik yapıdadır ve nesne oluşturmadan ihtiyacımız olan cebirsel ifadeleri kullanabiliriz.

static anahtar sözcüğünü kullanırken erişim belirleyicisinden önce ya da sonra koymamız bir fark yaratmaz

bir static metot içinde diğer static metotlar çağrılabilir ama normal bir metot çağrılamaz. Çünkü normal metotlar static olmayan nesneler üzerinden işlem yapar.

Not: static olmayan elemanlara ulaşabilmek için bir nesne yaratılmalıdır.

Static Değişkenler

static değişkenler genellikle bir sınıfı global düzeyde ilgilendiren özellikler için kullanılır. Örneğin bir sınıf içinde birden fazla metot tarafından kullanılacak özellik ihtiyacımız varken ya da oyun programlarken oyuncu sayısını tutan bir özelliğe ihtiyacımız varken kullanabiliriz.

Statik değişkenleri bildirmek için static anahtar sözcüğünü kullanırız.

static değerler tanımlandıklarında varsayılan değerler atanır. PersonalManager sınıfındaki static değişken olan TotalPerson ekrana yazdırıldığına 0 olacaktır. (int veri türünün varsayılan değeri 0’dır)

Statik üyelere ancak static bir metot içerisinden erişebiliriz.

Önemli: Her sınıf nesnesi oluşturulduğunda her static için bellekte ayrı ayrı yer tahsis edilmez.

Statik Yapıcı Metotlar

Normal metotlar gibi yapıcı metotlarda statik özellikte olabilir. static yapıcı metot ile bir sınıftaki statik değişkenler ile ilgili işlem yapmak için kullanılır.

  • static yapıcı metotlar diğer static metotlar gibi kullanılır. Bildirimin başına static anahtar sözcüğü getirilir.
  • Herhangi bir parametre almazlar.
  • Erişim belirleyicileri yoktur

Statik yapıcı metodu olan bir sınıftan nesne yaratıldığında o nesnenin varlığını kullanarak çağırabileceğiniz tek statik yapıcı metottur.

Not: Bir nesneyi hangi yapıcı metot ile oluşturursak oluşturalım static yapıcı metot mutlaka ilk nesne tanımlandığında çalışır.

Static Sınıflar

C# 2.0 ile birlikte C# diline static sınıf kavramı gelmiştir. Bir nesneye bağlı kalmadan yapmak istediğimiz işleri static olarak tanımlaya biliyorduk. İçinde sadece static üye ve metot bulunduran sınıfları static sınıf olarak tanımlayabiliriz. Bu bir zorunluluk değildir ancak iyi bir programlama göstergesidir.

  • Bir sınıf static olarak belirtildiği andan itibaren o sınıftan bir nesne yaratılmaz.
  • Static sınıflar içinde static olmayan metot veya özellik ve static yapıcı metot tanımlanamaz.
  • Static sınıflar kalıtım yoluyla türetilemez.
  • Static sınıflardan referans tanımlanamaz.

Static sınıflar dile eklenmiş kısıtlayıcı bir etkendir. Bu sayede kod okunabilirliği açısından önemli bir bildirim yöntemidir ve bir programcı static sınıf bildirimi gördüğü anda bütün üye elemanları ve özelliklerinin static bildiriminde olacağını anlayacaktır.

using Deyimi ile Statik üyelere Direkt erişim

C# 6.0 ile birlikte kod yazım kolaylığı sağlayan bir özellik gelmiştir. Bu sayede using anahtar sözcüğü ile isim alanında bulunan static nesnelere direkt erişebiliriz.

Önemli: Her ne kadar bu özellik kolay gibi gelse de kod okunula-bilirliği açısından pek tavsiye edilmiyor.

const ve readonly Elemanlar

Değerinin değişmeyeceği bilinen elemanlar sabit olarak bilini. Sabit değişkenler C# dilinde const anahtar sözcüğü ile bildirilir.

sabit ifadelerin değerleri derleme aşamasında bilinmelidir.

sabit değişkenleri değiştirmeye çalışmak derleme zamanında hataya yol açar

sabit ifadeler referans tipinde olamaz. Çünkü referans tiplerin değerleri çalışma zamanında belli olur ancak String türü bu duruma istisnadır.

referans tipleri için sabitleri tanımlamada readonly anahtar sözcüğü kullanılır. readonly (sadece okunur) tanımlanan değişkenler global düzeyde olacak diye bir şey yoktur.

static değişkenleri readonly olarak tanımladığımızda const anahtar sözcüğünün referans tipleri için bir versiyonu yapılmış olur.

Önemli: static readonly ve const olarak tanımlanan değişkenlerin tek farkı ilk değer verilme zamanıdır. const değişkenler derleme zamanında değerleri belirlenirken, readonly değişkenler çalışma zamanında ilk değer belirlenir.

Bu yazımda Sınıf (class) hakkında edindiğim bilgileri paylaşmaya çalıştım. Umarım faydalı olmuştur 🙂

Bir sonraki yazımda görüşmek üzere 🙂

Kaynaklar:

Her Yönüyle C# 7.0, Sefer Algan, Pusula Yayincilik

https://docs.microsoft.com/tr-tr/dotnet/csharp/programming-guide/classes-and-structs/classes

Leave a Comment