C# Dilinde Özellikler
C# Dilinde Özellikler – 1
Nesne yönelimli programlamanın günümüzde ne kadar yaygın olduğunu programlama ile ilgilenen herkes bilmektedir. Nesne Yönelimli Programlama (NYP) yaklaşımında temel olan prensiplerden birisi bilgi gizleme (information hiding)’dir. Bu prensibi projelerimizde uygulamak için C#’in sunduğu en önemli araçlardan biri olan sınıf özellikleri (class properties) konusunu inceleyeceğiz.
Bildiğiniz gibi, C# dilinde tasarlanmış bir sınıfta iki temel unsur bulunur. Birincisi sınıfın özellikleri (fields), ikinicisi ise sınıfın metodlari (methods)’dır. Herhangi bir sınıfın özellikeri sınıfta tutulan ilişikili verilerlerdir. Diğer taraftan sınıfın bizim için değişik işleri yapmasını metodları vasıtasıyla sağlarız. Sınıf tasarımı çok önemli bir iş olup; deneyim, konsantrasyon ve dikkat ister. Sınıfımızın özelliklerini tutan veriler, program akışı sırasında sınıf dışında değiştirilebilir veya bu değerlere ulaşmak istenebilir.
Bu durumda akla ilk gelen çözüm sınıfın verilerinin hepsinin dışarıdan ulaşilabilmesini ve değiştirilebilmesine olanak sağlayan public anahtari ile tanımlamakdir. Aşağıdaki programda bu tür bir çözümün uygun olabileceği düşünülmüştür:
| using System;
namespace Property_Makale public Otomobil(int model, string marka, string renk) this.marka=marka; public void OzellikleriGoster() class OtomobilTest oto1.model=300; oto1.OzellikleriGoster(); |
Yukarıdaki kod örneğimizde iki tane sınıf bulunmaktadır. Otomobil sınıfı ile otomobil nesnelerimizi oluşturabiliriz. Ayrıca bu sınıfın OzellikleriGoster() metodu ile herhangi bir otomobil nesnemizin özelliklerini görmek için ekrarana yazdırıyoruz. İkinci sınıfımızda (OtomobilTest) ise Otomobil sınıfımızdan nesneler oluşturmak ve onların özellikerini çağırmak için kullanacağız. Şimdi isterseniz Main() fonksiyonunu incelemeye başlayalım. Metodun hemen ilk başında oto1 isimli nesnemizi oluşturuyoruz. oto1 nesnemizin özellikleri 2000 model, siyah ve BMW olsun. Bir sonraki satırda oto1 nesnemizin modelini 300 yapıyoruz. İşte burda büyük bir hata yapılıyor! Çünkü 300 yılında henüz otomobil üretilmemişti. Böyle bir hatayı nasıl önleriz? Çözüm olarak otomobil nesnemizin herhangi bir özelliğini değiştirmek için ayrı bir metod yazmamız gerekir. O zaman programızı şu şekilde değiştirmemiz gerekiyor:
| using System;
namespace Property_Makale public Otomobil(int model, string marka, string renk) this.marka=marka; public void OzellikleriGoster() public void ModelDegistir(int yeniModel) class OtomobilTest oto1.ModelDegistir(300); |
Yukarıdaki programda Otomobil sınıfına ModelDegistir(int yeniModel) metodunu ekledik. Bu metod ile modeli değiştirilecek nesnenin modelinin şu anda bulunulan yıldan sonra ve 1900’den önce yapılmak istendiğinde hata mesajı veriyor ve modelini değiştirmiyor. Ayrıca sınıf içindeki model değişkenin tanımlanmasında private anahtarını da kullandığımıza dikkat ediniz. Bu şekildeki bir yaklaşım ile hem sınıfın iç işleyişini sınıf dışından saklamış oluyoruz hem de sınıfa ait verilerin değiştirilmesini sırasındaki hataları en az seviyede tutmayı sağlıyoruz.
Fakat yukarıdaki yöntemi genelde C++ programcıları kullanır(dı). Bizler C# programcıları olarak daha gelişmiş bir yola sahibiz. Sınıf içindeki değerleri değiştirmek ve ulaşmak için özellik (Property) aracını kullanırız. Aşağıdaki program ise C#’ın özellikleri nasıl kullandığına bir örnektir:
| using System;
namespace Property_Makale public Otomobil(int model, string marka, string renk) this.marka=marka; this.renk=renk; public void OzellikleriGoster() public int Model set class OtomobilTest oto.Model=300; |
Yukarıdaki programı çalıştırdığımızda aşağıdaki sonucu alırız:
Çıktısını gördüğünüz program kodunda C#’ın özellik tanımlama ve kullanma yöntemini kullandık. Özellikler ile herhangi bir nesneye ait değişkenin değerini öğrenebilir ve değiştirebiliriz. Yukarıdaki örnek kodda yeralan aşağıdaki kısımda bir özellik(Property) tanımlıyoruz. Genellikle bir özelliğin ismi üzerinde iş yaptığı değişkenin ismi ile aynı olup sadece ilk harfi büyük olur. Aslında bu bir zorunluluk değil. Sadece C#’da bu bir gelenektir. Bu şekilde bir kullanım bizim kodumuzu okuyanların kodu daha kolay anlaması ve bizim başkalarının kodlarını daha kolay anlamamıza yardımcı olur. Bir özelliğin tanımında özellik isminden önce ne tür bir değer dönderecekse onun tipini belirtmeliyiz. Bu genelde özelliğin ilgili olduğu değişkenin tipidir 🙂
| public int Model { get { return model ; } set |
Özellikler içinde get ve set olmak üzere iki ayrı blok kod olur. İstersek sadece get veya sadece set blokları olan özellikler de yazabiliriz. get bloğunda ilgili değişkenimizin değerini dışarıya döndeririz. set bloğunda ise değişkenimizin değerini değiştiririz. Burda gereken kontrolleri yapıp daha sonra uygunsa girilen değeri kabul edebiliriz. Eminimki get bloğu içinde dikkattinizi değişkeni ismi yerine value şeklinde çağırmamız çekmiştir. Bu sayede kod içinde karışıklık olmaz. Zaten sadece bir tane değişken üzerinde çalışıyorsunuz.
Bu makalemizde C# dilinde yeralan özellik (Property) kavramını inceledik. Bir ilerleyen makalelerimizde sadece yazılabilir (read-only) ve sadece okunabilir (write-only) özellik yazmayı ve kullanmayı inceleyeceğiz.
WinAPI Fonksiyonlarının C#’ta Kullanımı
C# ve dotNET ile birlikte yazılım geliştirmeye yeni bir soluk gelmiş olsada C# ile eskiden yazılmış COM komponentlerine erişebilmek mümkündür. Daha önceki iki makalede .NET ve COM ilişkisini detaylı bir şekilde incelemiştik. Bu makalede .NET’in Win 32 API ile nasıl entegre edildiği anlatılacaktır. .NET ve C#’ın yeni imkanlarının yanısıra eski bir teknoloji olan COM ve yönetilmeyen(unmanaged) kodlarla uyumlu bir şekilde çalışması belkide C# ve .NET’i diğer yazılım geliştirme platformlarından ayıran en önemli özelliktir.
Bildiğiniz gibi C#’ta gösterici kullanımı tamamen serbesttir. Bu yüzden eskiden(.NET öncesi) yazılmış ve parametre olarak gösterici alan COM komponentleri ve Windows API fonksiyonları C# ile sorunsuz bir şekilde çalıştırılabilmektedir. Bu yazıda Win API fonksiyonlarının .NET ortamında ne şekilde ele alındığı incelenecektir.
Win32 sistem fonksiyonları kullanıldığında, kod CLR tarafından yönetilmekten çıkar. .NET ortamında geliştirilen bir uygulamada yönetilmeyen kod segmenti ile kaşılaşılırsa ilgi kod segmenti CLR tarafından yönetilmekten çıkar. Dolayısıyla “garbage collection” mekanizması ve .NET’e özgü diğer servisler kullanım dışı olur.
CLR tarafından yönetilmeyen kodlara erişebilmek için C#’ta System.Runtime.InteropServices isim alanında bulunan ve DllImprtAttribute sınıfını temsil eden DllImport niteliği kullanılmaktadır. DllImport niteliği ile harici bir kaynakta bulunan metoda referans vermek için external anahtar sözcüğü kullanılır. Bir sınıf bildiriminin en başında external anahtar sözcüğü ve DllImport niteliği kullanılarak CLR tarafından yönetilmeyen bir metot bildirimi yapılır. Tabi metodun gövdesi harici bir kaynakta zaten var olduğu için bizim metodun gövdesini yazmamızın bir anlamı yoktur. Ardından bu metot sınıfın istenildiği yerinde kullanılabilir. İsterseniz basit bir örnekle DllImport niteliğinin kullanımını gösterelim.
Win API windows sistemlerinin programlanabilir arayüzünü içermektedir. Windows uygulamarının tamamı bu arayüzdeki fonksiyonları ve diğer yapıları kullanmaktadır. Aşağıdaki programda Win32 sistemlerinde bulunan MessageBox() fonksiyonunun kullanımına bir örnek verilmiştir.
Not : Bu yazıda C#’ta niteliklerin(Attributes) nasıl kullanıldığını bildiğiniz varsayılmıştır.
| using System; using System.Runtime.InteropServices; class Class1 static void Main() |
DllImportAttribute sınıfının bir tane yapıcı metodu bulunmaktadır. Bu metot parametre olarak harici kaynağın adı belirtmektedir. Yukarıdaki kaynak kodda MessageBox fonksiyonunun bulunduğu “user32.dll” isimli dosya DllImport niteliğine parametre olarak verilmiştir. Bu örnekte dikkat edilmesi gereken diğer nokta ise extern anahtar sözcüğünün kullanımıdır. Bu anahtar sözcük ile bildirimi yapılan metodun harici bir dosyada olduğu belirtilmektedir. Dolayısıyla C# derleyicisi metodun gövdesini kaynak kodda aramayacaktır.
Programın çalışma şeklini açıklamadan önce ekran çıktısına bakalım :
Yukurıdaki çıktıdan ve kaynak koddan da görüldüğü üzere Win API deki bir fonksiyonun çağrımı klasik metot çağrımından farklı değildir. Değişen tek şey metodun bildirim şeklidir.
Not : Gösterilen mesaj kutusunun farklı formlarını görmek için MessageBox metodunun parametreleri ile oynayın.
Şimdi kısaca yukarıdaki programın çalışma zamanındaki durumunu inceleyelim. Program çalıştırıldığında, CLR tarafından yönetilmeyen bir metot çağrımı yapıldığında ilgili kaynaktan metot belleğe yüklenir ve belleğe yüklenen metodun başlangıç adresi saklanır. Ardından bizim parametre olarak geçtiğimiz değişkenler DLL’ deki fonksiyona uyumlu hale getirilir ve parametre olarak geçirilir. Eğer bir geri dönüş değeri bekleniyorsa yönetilmeyen kod bölümünden gelen değer uygun .NET türüne dönüştürülerek işlemlere devam edilir. Bu işlemler tamamen .NET’in alt yapısını ilgilendirmektedir. Dolayısıyla programcının yapacağı iş sadece metodun bildirimini doğru bir şekilde gerçekleştirmektir.
DllImportAttribute sınıfının bir kaç önemli özelliği daha vardır. Bunlardan en önemlisi harici kaynaktaki bir fonksiyona takma isim verebilmemizi sağlayan string türünde olan EntryPoint özelliğidir. EntryPoint özelliğini kullanarak MessagBox fonksiyonuna takma isim verebiliriz. Fonksiyon çağrımı bu takma isim ile gerçekleştirilebilmektedir. Örneğin MessageBox fonksiyonuna TebrikMesajiVer şeklinde bir takma isim vermek için aşağıdaki gibi bir bildirim yapılmalıdır.
| using System; using System.Runtime.InteropServices; class Class1 static void Main() |
Şimdi de DllImport niteliği ile ilgili diğer özelliklere ve önemli noktalara bakalım.
1 – DllImport niteliği yalnızca metotlara uygulanabilir.
2 – DllImport niteliği ile işaretlenmiş metotlar mutlaka extern anahtar sözcüğü ile bildirilmelidir.
3 – DllImport niteliğinin EntryPoint’in haricinde 4 tane isimli parametresi(named parameter) daha vardır. Bunlar : CallingConvention, CharSet, ExactSpelling, PreserveSig ve SetLastError parametreleridir. Bu parametrelerden önemli olanlar aşağıda açıklanmıştır.
4 – CallingConvention : Bu paramtre CallingConvention numaralandırması türündendir. Bu parametre ile harici metodun ne şekilde çağrılacağı ile ilgili bilgi verilir. CallingConvention numaralandırması 5 tane sembol içerir. Varsayılan olarak bu sembol Winapi olacak şekilde ayarlanmıştır. Yani CallingConvention parametresini DllImport ile kullanmıyorsak varsayılan olarak bu Winapi’dir. Diğer semboller ise Cdecl, FastCall, ThisCall, StdCall şeklindedir. En çok Winapi sembolu kullanıldığı için diğer sembollerin ne anlama geldiği anlatılmayacaktır. Diğer sembollerin ne anlama geldiklerini MSDN kütüphanesinden detaylı bir şekilde öğrenebilirsiniz.
5 – CharSet : Harici fonksiyonun çağrımında kullanılacak karekter setini belirler. Bu parametre CharSet numaralandırması ile belirtilir. Varsayılan olarak CharSet.Auto şeklindedir. CharSet numaralandırmasının diğer sembolleri Ansi, Unicode ve None şeklindedir.
6 – ExactSpelling : EntryPoint ile belirtilen ismin ilgili fonksiyon ismine yazım biçimi bakımından tam uyumlu olup olmayacağını belirtir. Bu özellik bool türündendir ve varsayılan olarak false değerdir.
DllImport metodunun varsayılan çağrım biçimi olan Winapi sadece Windows sistemlerine özgün olduğu için sistemler arası taşınabilirliğin yüksek olması gereken projelerde bu niteliğin kullanımından kaçınmak gerekir. Bu yüzden DllImport özelliğini kullanmadan önce ilgili fonksiyonun .NET Framework içinde olup olmadığını kontrol etmek gerekir. Örneğin MessageBox fonksiyonu zaten System.Windows.Forms isim alanında bulunduğu için API kullanarak bu fonksiyondan yararlanmak mantıklı değildir. Zira ileride programınızın Linux ortamında yada diğer farklı ortamlarda da çalışmasını istiyorsanız programınızı değiştirip yeniden derlemeniz gerekecektir. Oysa .NET Framework içinde bulunan standart sınıfları ve onların metotlarını kullanırsanız böyle bir derdiniz olmayacaktır.
C#’da Sıra (Queue) Sınıfı ve kullanımı
Bir önceki yazımızda genel olarak yığın (Stack) veri yapısının çalışma modeline ve C#’ta kullanabileceğimiz yığın sınıfını ve bu sınıfın metodları üzerinde durmuştuk. Şimdi burada ise, diğer önemli veri yapısı olan sıra (queue) veri yapısını inceleyeceğiz. Queue veri yapısının mantığını anladıktan sonra .NET sınıf kitaplıklarında bulunan Queue sınıfını öğreneceğiz.
| Queue veri tipi ve .NET’in sınıf kütüphanesinde bulunan Queue sınıfı, yığın (stack) sınıfına çok benziyor. Bu veri tipleri çalışma mantığı olarak tam tersi gibi görünmelerine rağmen bir çok metodları ve özellikleri birebir örtüşüyor. Bu durumda birazdan okayacağınız makalenin formatı yığın makalemizdekine çok benzediğini göreceksiniz. |
1. Queue(sıra) veri Yapısının Çalışma Şekli
Sıralar (queue) İlk Giren İlk Çıkar (FIFO) prensibi ile çalışan veri yapısı şekinde bilinirler. Bir sıraya ilk giren eleman ilk olarak çıkar. Sıralara örnek olarak bir markette alışverişini yapan müşterilerin, aldıkları ürünlerin ücretlerini ödemek için kasada sıraya geçmeleri verilebilir. Marketteki sırada sıraya ilk giren müşterinin işi ilk önce biter. Daha sonra ikinci ve üçüncü müşterilerin işleri yapılır.
Sıralar bilgisayar programlamada sık sık başvurulan veri yapılarıdır. Mesela, işletim sisteminde yapılması gereken işleri bir sıra veri yapısı ile tutarız. Herhangi bir anda yeni bir iş geldiği zaman bu iş sıraya (Queue) girer. Sırası gelen iş yapılır ve sonraki işe geçilir gibi. Queue veri yapıları ayrıca simulasyonlarda da sık sık kullanılır.
2. .NET Sınıf Kütüphanesi Sıra Sınıfı (Queue)
.NET sınıf kütüphanesinde sıra veri yapısını kullanmak için Queue sınıfını kullanırız. NET’in yığın (Stack) sınıfını kullanmak için pogram kodunun baş tarafına using System.Collections; eklememiz gerekir. Yani sıra sınıfı System.Collections isim alanında bulunuyor.
C# veya herhangi bir dilde yazılan yığın veri yapılarında Enqueue(), Dequeue, Peek(), Clear() fonksiyonları ve Count, özelliği vadır. Bunların yanında Clone(), CopyTo(), ToArray(), Contains() ve Equals() metodları .NET’in yığın sınıfında yeralır.
Sıra sınıfının Enqueue() metodu sıraya yeni bir eleman ekler. Dequeue() metodu ile yığının en öndeki elemanı sıradan siler ve silinen elemanı geriye dönderir. Eğer sıranın tepesindeki elemanı öğrenmek istersek Peek() medotunu işimize yarar. Bu metod sıranın başındaki nesneyi dödürür ama bu nesneyi sıradan silmez.
| using System; using System.Collections; // Queue sınıfı bu isim alanı içinde bulunur. class Sira_Ornek1 public static void Main() // Queue sınıfından bir nesne oluşturalım: // Nesnemize Enqueue metodu ile değerler girelim: // sira isimli nesnemizin eleman sayısı: // sira isimli nesnemizin elemanları: //sira isimli nesmemizden bir eleman alalım: //şimdi ise siranin en başındaki nesneyi öğrenelim. } public static void DegerleriYaz( IEnumerable kolleksiyon ) while ( Enum.MoveNext() ) Console.WriteLine(); |
Yukarıdaki örnek programda önce Queue sınıfından sıra isimli bir nesne oluşturuyoruz. Sonraki altı satırda sıramıza “Ahmet”, “Ferit”, “Hasan”, ve “Hüseyin” değerlerini Enqueue metodu ile ekliyoruz. Degerleri() ismini verdiğimiz static fonksiyonumuz ile sıramızdaki eleman sayısını ve elemanları ekrana yazdırıyoruz. Daha sonra sıramızdan bir tane elemanı Deuque() metodu yardımıyla alıyor ekrana yazdırıyoruz. Programın son kısmında ise Peek() metodunu kullanrak sıranın en üstündeki elemanın ne olduğunu öğreniyoruz ve bu eleman sırada kalıyor.
Sıra sınıflarında bulunan diğer iki temel fonksiyonlar olan Count özelliği ve Clear() metodlarıdır. Bunlardan Count, sıra nesnesinde bulunan elemanların sayısını geriye dönderen bir özelliktir. Özellikler C# dilinde sınıflarda bulunan üye değişkenlerin değerlerini öğrenmemize ve onların değerlerini değiştirmemize yarayan bir tür fonksiyonlardır. Count özelliği eleman sayısını int tipinde dönderir ve sadece okunabilen (readonly) yapıdadır. Özellikler program içinde çağrılırken parantezleri kullanmayız. Eğer sırayı boşaltmak/temizlemek istersek Clean() metodu işimizi yarayacaktır. Clean() metodu hiçbir parametre almaz ve hiçbirşey döndermez. Herhangi bir sıra nesnesinin içinde bir elemanın olup olmadığını anlamak için Contains() metodu kullanılır. Bu metod aranacak nesneyi alır ve geriye true veya false değerlerini dönderir. İsterseniz aşağıdaki programda Contains() ve Clear() metodları ile Count özelliklerini nasıl kullanabileceğimizi görelim:
| using System; using System.Collections; // Queue sınıfı bu isim alanı içinde bulunur. class Sira_Ornek2 public static void Main() // Queue sınıfından bir nesne oluşturalım: // Nesnemize Enqueue metodu ile değerler girelim: // sıra isimli nesnemizin eleman sayısı: // sira isimli nesnemizin elemanları: //Contains metodunun kullanımı: if(sira.Contains(7)) // sıramızın içindeki elemanları silelim: // Sıramızı temizledikten sonra kaç tane Console.WriteLine(“\n Sıramizda şu anda {0} tane eleman vardır.”, elemanSayisi); } public static void DegerleriYaz( IEnumerable kolleksiyon ) while ( Enum.MoveNext() ) Console.WriteLine(); |
Yığında makalemiz Clone(), ToArray() ve Equals() metodlarının aynıları Queue sınıfı içinde bulabiliriz. Sizlerin yığın makalesindeki en son örneğini sıra veri tipi için de yazıp kendinizi denemenizi tavsiye ederim. Herkese iyi çalışmalar.
