13 Mayıs 2012 Pazar

C# VİDEO KONU ANLATIMLARI 24 DERSLİK


C# Karşılaştırma ve mantıksal operatörler
    

C# Değişkenler 6
       
           
C# Değişkenler 3
       
C# For Döngüsü Kullanımı 2
        

C# For Döngüsü Kullanımı 
         

C# For Döngüsü Kullanımı 2
       

C# For Döngüsü Kullanımı 2
         

C# For Döngüsü Kullanımı 2
         

C# For Döngüsü Kullanımı 3
         

C# Form Bileşeni
           

C# Form Bileşeni 2
           

C# if else kullanımı 2
           

C# if else kullanımı
           

C# if else kullanımı 2
             

C# if else kullanımı 3
             
C# if else kullanımı 4
            

C# if else kullanımı 5
             

C# if else kullanımı 6
               

C# Merhaba Dünya
                 

C# Switch Case Kullanımı
                  

C# Toplama İşlemi
                 

C# Tür Dönüşümleri 2
                

C# Tür Dönüşümleri
                

C# Tür Dönüşümleri 3
                  

11 Mayıs 2012 Cuma

C# KİTABI


Her Yönüyle ilk C# Programımız
C# dili ortaya çıkalı daha hiç birsey yapmayan varsa ya da birseyler yapıp da ne
yaptığından emin olmayan varsa iste bu yazı tam size göre. Bu yazımızda klasik Merhaba
Dünya programımızı yazacağız.Ama programımızı yazarken her seyi adım adım
öğreneceğiz. Unutmayın amacımız burada Merhaba Dünya yazmak değil. :) O halde
asağıdaki programı iyice inceleyin önce, söyle bir süzün programı yukarıdan asağıya,
fazla detaylara inmeden yazımızı okumaya devam edin;
//dosya adı : Merhaba.cs
using System;
namespace MerhabaDunya
{
class Sınıf1
{
static void Main(string args[])
{
Console.WriteLine("Merhaba Dünya");
}
}
}
Yukarıdaki ilk programımızı incelediğinize göre açıklamalarımıza geçebiliriz. Eğer önceden
C++ ve Java ile ilgilenmis arkadaslar varsa yukarıdaki kodlar tanıdık gelebilir. Nitekim,
her ne kadar Microsoft firması ilk baslarda bunu kabul etmese de C# dili Java ve C++
dillerinin harmanlanmasından olusmus bir dildir. Bugün bunu kabul etmeyen yoktur
sanırım.
Yukarıdaki ilk programımızın kodunu ben Notepad ile yazdım. Ama kodu derleyip
çalıstırmak için bir C# derleyicisine ihtiyacımız olacak. C# derleyicisi Visual Studio.NET ile
kurulabileceği gibi www.microsoft.com web sitesinden .NET Framework yazılımını
indirerek de kurulabilir. Eğer Visual Studio ortamında çalısıyorsanız yukarıdaki kodları
Visual Studio .NET ' in sunduğu hazır proje sablonlarından rahatlıkla olusturabilirsiniz.
Visual Studio programını çalıstırdıktan sonra Project->New menüsünden dil olarak Visual
C# ve proje sablonu olarak da "Console Application" seçerseniz, main islevi içindeki
kodlar dısındaki yapı otomatikmen olusturulacaktır.Eğer .NET Framework yapısını
kurduysanız Console Ekranından C# derleyicisini çalıstırmalısınız. Komut ekranını <csc
Merhaba.cs> yazarak kaynak kodumuzu derleyebilirsiniz.
Simdi kodlarımızı inceleyelim. Đlk satırdaki <using System;> ifadesi System adlı bir isim
alanının kullanılacağını belirtiyor.Peki nedir bu isim alanı(Namespace). Đsimalanı kavramı
son yıllarda program modüllerinin çok sayıda artmasından dolayı popüler hale gelmistir.
Kolay ve hızlı programlama yapmamızı sağlayan bir takım hazır kütüphaneler her ne
kadar isimizi kolaylastırsa da eğer isimalanları olmasaydı kullanacağımız her kütüphane
bizim için isin içinden çıkılmaz bir hale gelebilirdi. Düsünün ki iki ayrı firma iki ayrı sınıf
kütüphaneleri olusturdu ve bu kütüphanelerin içinde aynı isimli birden çok sınıf yapısı var.
Eğer biz programcı olarak iki firmanın da kütüphanesini kullanmak istiyorsak her ikisini
aynı kod içinde kullanamayız. Çünkü aynı isimli sınıflar derleme asamasında hata
verecektir. Bu durumda yapılması gereken tek sey ya da en etkili yöntem isimalanlarını
kullanmaktır. Yani bir sınıfa(class) ulasabilmek için onun isim alanıyla çağırmak. Đsim
alanları hiyerarsik yapıda olabilir. Mesela System isim alanının altında baska bir isim alanı
onun altında baskaları vs. Đste .NET isimalanı(namespace) hiyerarsisinin en tepesinde
bulunan isim alanı System adlı isimalanıdır. En temel islemlerimiz için bile bu isim alanını
kullanmalıyız. Aksi halde programımız çalısmayacaktır. Đsimalanlarını kullanmak için
isimalanının basına using sözcüğü getirilir.
Soru: System isim alanının içinde Data isimalanında bulunan bir cs adlı sınıfı
kullanabilmek için kaynak kodumuza ne eklememiz gerekir.
Cevap : Kaynak kodumuzun en basına asağıdaki ifadeyi yazmamız gerekir.
using System.Data;
Bildiğiniz gibi C# dili %100 nesne tabanlı bir dildir. Yaptığımız hersey bir sınıf nesnesidir
C# dilinde. Nesne olmayan hiçbirsey yoktur. C++ dilindeki main islevini hatırlarsınız
çoğunuz. Programımız c++ dilinde main islevinden baslar ama main islevi hiç bir zaman
bir sınıf içinde olmamıstır.C# dilinde hersey sınıflarla temsil edildiği için main islevi de
bizim belirlediğimiz bir sınıfın islevi olmak zorundadır. Yukarıdaki programımızda <class
Sınıf1> ifadesi ile programımızda bir sınıf nesnesi olusturuyoruz. Sınıf1 sınıfının bir islevi
olan main'in elbette eskiden de olduğu gibi özel bir anlamı vardır. Biliyorsunuz ki
derleyiciler programın nerden çalısacağını bilmek isterler, aksi halde derleme isleminden
sonra "programınız için baslama noktası bulunamadı" hatası alırız. Bu yüzden main islevi
bizim için eskiden de olduğu gibi programımızın baslangıç noktasıdır. Yani biz programda
yapmak istediklerimizi main islevi içinde gerçeklestireceğiz. Sınıf tanımlamalarımızı ise
istediğimiz noktada yapabiliriz. Daha öncede dediğimiz gibi isimalanları birçok sınıfın veya
tek bir sınıfın olusturduğu kümedir. Bizim ana programımız da bir sınıf olduğuna göre
Class1 sınıfını istediğimiz isimli bir isimalanına sokabiliriz. Yukarıda <namespace
MerhabaDunya> yazarak isimalanını baslatıyoruz.
Simdi main islevinin içine bakalım, System isimalanında bulunan Console sınıfının bir
metodu olan WriteLine() ile ekrana bir string ifadesi yazdırıyoruz. Biz burda iki tırnak
ifadesi içinde yazımızı belirtmemize rağmen fonksiyonun kullanmı bununla sınırlı değildir.
C# dilindeki fonksiyon asırı yükleme (function overloading)kullanılarak fonksiyonu birçok
parametrik yapıda kullanabilmemiz sağlanmıstır. Fonksiyon asırı yükleme konusuna
bundan sonraki yazılarımızda değineceğimizi belirtelim. WriteLine() islevinin adından da
anlasılacağı gibi ekrana basmak istediğimiz yazıdan sonra satır atlama islemi yapar.Bunu
test etmek için bir tane "Merhaba Dünya" da siz yazdırın. Göreceksiniz ki siz
belirtmemenize rağmen alt alta iki tane "Merhaba Dünya" yazısı çıkacak.
Eğer bu programı yazıp derlediyeseniz ne mutlu size ki C# dünyasına güzel bir adım
attınız.
Visual C# ile Windows Menüleri Hazırlama
Merhaba, bu makalemizde, hemen hemen tüm Windows uygulamalarının temel yapı tası
olan Windows menülerinin nasıl hazırlandığını ve basit bir uygulamasını adım adım
göreceğiz. Bildiğiniz gibi Windows menülerini simdiye kadar Visual Basic ortamında çok
basit bir sekilde yapmak mümkündü. Ama artık Visual C# ile menü hazırlamak hem daha
kolay hem de daha eğlenceli. Bu makalede yapacağımız uygulamadaki amacımız, File ve
Edit bölümünden olusan Windows menüsünü tek bir Windows butonuyla aktif ya da pasif
duruma getirmek.
Simdi uygulamamızın ilk adımı olan yeni proje olusturma sayfasını açalım.
File->New -> Project menüsünü kullanarak asağıdaki gibi yeni bir proje olusturalım.
Proje tipi olarak Visual C# Project, template olarak da Windows Application seçtikten
sonra projemize uygun isim verip OK butonuna tıklayalım.
Projemizi olusturduğumuzda Visual C# IDE 'sinin bizim için bir baslangıç formu
olusturduğunu görürüz. Bu form doğal olarak su anda bostur. Toolbox menüsünü
kullanarak Form üzerine istediğimiz kontrolleri sürükle bırak yöntemiyle yerlestirebiliriz.
Ya da istediğimiz kontrolü çift tıklayarak da aynı islevi gerçeklestirebiliriz. Eğer toolbox
menüsünü göremiyorsanız ekranın sol alt küsesinde bulunan
ToolBox ikonuna tıklayın. Simdi formumuza basit bir MainMenu kontrolu ekleyelim. Yine
ToolBox menüsünden asağıdaki ikona sekline benzeyen kısma çift tıklayın. Eğer islem
basarılıysa formunuzun en üst kısmında edit edilmek üzere bir menü olusacaktır.
MainMenu üzerine fare ile gelerek istediğiniz menü elemanlarını ekleyin.Ben önce File
elemanını sonra Edit elemanını ve Edit elemanının içinde de Copy ve Paste menü
elemanlarını asağıdaki gibi olusturdum.
Simdi menü elemanlarımıza Properties penceresinden isim verelim. Asağıda gördüğünüz
pencereden form design penceresinden seçtiğiniz elemanla ilgili özelliklere
ulasabilirsiniz.Simdi Edit menü elamanına tıklayarak Properties ekranındaki name
özelliğine "menuEdit" yazalım. Burda menu elemanına verdiğimiz ismi daha sonra kod
yazarken kullanacağımız için aklımızda kalacak bir isim vermemiz düzenli kod yazmak için
önemli bir sebeptir. Menü elemanlarıyla isimiz bittiğine göre sıra menüyü kontrol
edeceğimiz butonu yerlestirmeye geldi. ToolBox penceresinden "Buton" a çift tıklayarak
forma bir buton yerlestirelim .Daha sonra butona tıklayıp Properties penceresinden buton
ismi (Name) olarak "BizimButon " yazalım. BizimButon ' un text özelliğine ise "MENU
PASĐF ET" yazısını yazalım. Bu yazıyı yazmamızın sebebi ise sudur: Mene elemanları
varsayılan olarak aktif durumdadırlar. Bu yüzden menüyü pasif hale getirmek için bu
yazıyı seçtik.
Evet,Form tasarım islemi bitti. Simdi sıra geldi BizimButon ile menüye aktif ve pasif
durumları arasında geçis yaptırmak. Tabi asıl iste simdi baslıyor.
Form üzerindeki butona çift tıklayarak kod yazma ekranına gelelim.
Gördüğünüz gibi Visual C# bizim için bir takım kodlar olusturdu. Biraz bu hazır kodları
ana hatlarıyla inceleyelim.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
Yukarıdaki kodlarla programımızın kullanacağı bir takım sistemler derleyiciye bildiriliyor.
public class Form1 : System.Windows.Forms.Form
System.Windows.Froms.Form sınıfından yeni bir Form1(bizim form) sınıfı türetilerek bu
form içindeki elemanlar tanımlanıyor.
private System.Windows.Forms.MainMenu mainMenu1;
private System.Windows.Forms.MenuItem menuFile;
private System.Windows.Forms.MenuItem menuEdit;
private System.Windows.Forms.MenuItem menuItem3;
private System.Windows.Forms.MenuItem menuItem4;
private System.Windows.Forms.Button BizimButon;
private System.ComponentModel.Container components = null;
private void InitializeComponent() islevi ile Form1 sınıfı içindeki elemanlarla ilgili ilk
islemler yapılıyor. Elemanların form üzerindeki yeri ve elemanlara ait Properties
penceresinden tanımladığımız bütün özellikleri bu islev ile yerine getirilir.
static void Main()
{
Application.Run(new Form1());
}
Uygulamamızın Form1 üzerinden gerçeklestirileceğini belirtir.
Đste bu da bizim kodumuz :
private void BizimButon_Click(object sender, System.EventArgs e)
{
if (menuEdit.Enabled)
{
menuEdit.Enabled=false;
BizimButon.Text="MENU AKTIF ET";
}
else
{
menuEdit.Enabled=true;
BizimButon.Text="MENU PASIF ET";
}
}
Bu kodu yazabilmek için form design penceresinden BizimButon çift tıklayarak
BizimButon_click() islevinin içine geçelim. Yukarıdaki kodda eğer menuEdit aktifse pasif
duruma getiriyoruz ve BizimButon 'a da "MENU AKTIF ET" yazıyoruz. Eğer menuEdit
zaten pasifse Menuyu aktif hale getirip BizimButon yazısını da "MENU PASIF ET"
yapıyoruz. Asağıda her iki durum için programımızın çıktısı mevcuttur.
C ve C++ bakıs açısıyla C# dili
Bildiğimiz gibi bilgisayarları programlamak için programlama dillerine ihtiyaç duyulur.Bu
dillerden en popülerleri Basic,C,C++,Pascal,Java ve Assembler 'dır.Makina dili ise
donanımı kontrol etmek için donanımı üreten firma tarafından tanımlanan komutlar
kümesidir. Bazı programlama dilleri derleyicilere ihtiyaç duymasına karsın bazıları ise
yorumlayıcılara ihtiyaç duyarlar, mesela bir c++ programını çalıstırabilmek için C++
derleyicisine ihtiyacımız varken, Perl ile yazılmıs bir CGI scripti için komut yorumlayıcısına
ihtiyacımız vardır. Derleyiciler programı çalıstırmadan önce kodları makina komutlarına
çevirirler fakat yorumlayıcılar bir grup kodu satır satır ya da bloklar halinde yorumlayarak
çalıstırırlar.
Aslında derleyiciler de, komut yorumlayıcıları da birer bilgisayar programından baska
birsey değildirler.Yani c ve c++ dilleri bir giris bekleyen ve çıkıs veren birer bilgisayar
programları gibi düsünülebilir.Giris olarak kaynak kodu veren bu programlar çıkıs olarak
ise makina kodu üretirler.
C ve C++ dillerine kısa bir bakıs:
C dili en popüler yapısal programlama dilidir.C dili Dennis Ritchie tarafından, Martin
Richards ve Ken Thompson tarafından gelistirilen BCBL ve B dillerinin temelleri üzerine
kuruldu.
C dili "The C Programming Language by Brian Kernighan and Dennis Ritchie" kitabıyla
büyümüstür.C dili için, 1983 yılının büyük önemi vardır.Çünkü 1983 yılında ANSI
standartlar komitesi C standartları için toplanmıstır.Bu standartlasma süreci tam 6 yıl
sürmüstür.Ve tabi ki su anki standartların olusumuna katkıda bulunan ANSI 99
standartları da diğer önemli bir gelismedir.
C programcılar tarafından herhangi bir tür program gelistirmek için yazılmıs genel amaçlı
bir dildir. C ile bir düsük seviyeli sistem için program yazabileceğimiz gibi, yüksek seviyeli
bir GUI(Grafik Arabirimi) tasarlamamız da mümkündür.Ve elbette kendi kütüphanemizi
de C ile olusturabiliriz.C dilinin ortaya çıkmasından bunca yıl geçmesine rağmen
popülaritesini hiçbir zaman kaybetmemistir. Günümüz programcıları çesitli amaçlar için
programlarını gelistirirken C dili ile yazılmıs kaynak kodlarını kullanırlar.
Bjarne Stroustrup 1980 yıllında C++ dilini ortaya çıkarmıstır. C++ dili C temelli ve C nin
bir üst kümesi olarak düsünülebilir. C++ en popüler nesne temelli programlama dilidir.
C++ dilinin ilk ismi "C with Classes"(C ile sınıflar) idi. C++ dili C diline nazaran daha etkili
ve güçlüdür.Ve en önemli özellği ise C 'den farklı olarak nesne temelli bir dildir.Su anda
C++ dili ANSI ve ISO kurulusları tarafından standartlastırılmıstır. Bu standartların son
versiyonu 1997 yılında yayınlanmıstır.
C# diline kısa bir bakıs:
C#, güçlü, modern, nesne tabanlı ve aynı zaman type-safe(tip-güvenli) bir programlama
dilidir.Aynı zamanda C#, C++ dilinin güçlülüğünü ve Visual Basic' in ise kolaylığını
sağlar.Büyük olasılıkla C# dilinin çıkması Java dilinin çıkmasından bu yana programcılık
adına yapılan en büyük gelismedir. C#, C++ 'ın gücünden , Visual Basic 'in kolaylığından
ve Java 'nın da özelliklerinden faydalanarak tasarlanmıs bir dildir. Fakat sunu da
söylemeliyiz ki, Delphi ve C++ Builder 'daki bazı özellikler simdi C# 'da var. Ama Delphi
ya da C++ Builder hiçbir zaman Visual C++ ya da Visual Basic 'in popülaritesini
yakalayamamıstır.
C ve C++ programcıları için en büyük sorun, sanırım hızlı gelistirememedir. Çünkü C ve
C++ programcıları çok alt seviye ile ilgilenirler.Üst seviyeye çıkmak istediklerinde ise
zorlanırlar.Ama C# ile artık böyle bir dert kalmadı.Aynı ortamda ister alt seviyede
isterseniz de yüksek seviyede program gelistirebilirsiniz.C# dili Microsoft tarafından
gelistirilen .NET paltformunun en temel ve resmi dili olarak lanse edilmistir.C# dili Turbo
Pascal derleyicisini ve Delphi 'yi olusturan takımın lideri olan Anders Heljsberg ve
Microsoft'da Visual J++ takımında çalısan Scott Wiltamuth tarafından gelistirilmistir.
.NET framework'ünde bulunan CLR (Common Language Runtime), JVM (Java Virtual
Machine)' ye, garbage collection, güvenilirlik ve JIT (Just in Time Compilation)
bakımından çok benzer.
CLR, .NET Framework yapısının servis sağlama ve çalısma zamanının kod organizasyonu
yapan ortamıdır. CLR, ECMA standartlarını destekler.
Kısacası C# kullanmak için CLR ve .NET Framework sınıf kütüphanesine ihtiyacmız vardır.
Bu da demek oluyor ki C#, JAVA, VB ya da C++ değildir. C,C++ ve JAVA 'nın güzel
özelliklerini barındıran yeni bir programlama dilidir. Sonuç olarak C# ile kod yazmak hem
daha avantajlı hem daha kolay hem de etkileyicidir.
Bir baska yazıda bulusmak ümidiyle ...
C#'da Đfadeler, Tipler ve Değiskenler
Bu derste C# dilindeki ifadeler,tipler ve değiskenler anlatılacaktır.
Dersimizin hedefleri :
• Değisken kavramının anlasılması.
• C# dilinde bulunan basit tiplerin öğrenilmesi.
• C# dilindeki ifadelerin temel olarak anlasılması.
• String veri tipinin öğrenilmesi.
Değiskenleri en sabit sekilde verilerin depolandğı yerler olarak tanımlayabiliriz.
Değiskenlerin içine verilerimizi koyabilirirz veya değiskenlerimizin içindeki verileri C#
programındaki islemlerimiz için kullanabilliriz. Değiskelerin tipini belirleyen faktör,
onların içerdikleri verilerin çesitleridir.
C# dilinde kullanacağımız her değiskenin bir tipi olmak zorundadır ( Vbscript ve
JavaScript gibi dillerde değisken tanımlarken onun tipini de ayrıca belirtmeye gerek
yoktur.) Bir değisken üzerinde yapılan tüm islemler de onun hangi tipte bir değisken
olduğu göz önüne alınarak yapılır. Böylece programda bütünlük ve güvenlik korunmus
olur.
Boolean ( doğru/yanlıs ), ve üç sayısal veri tipi; integer( tamsayı ), floating point
(ondalıklı sayı ) ve decimal( muhasebe ve finansal islemler için) C# dilinin en basit veri
tipleri olarak sayılabilir.
Kod 1 : Boolean değerlerin görüntülenmesi : Boolean.cs
using System;
class Booleans
{
public static void Main()
{
bool content = true;
bool noContent = false;
Console.WriteLine("It is {0} that C# Station provides C# programming language
content.", content);
Console.WriteLine("The statement above is not {0}.", noContent);
}
}
Yukarıdaki Kod 1’de de görüleceği gibi boolean değiskenler ya true(doğru) veya
false(yanlıs) değerlerini alabilirler. Programın çıktısı söyle olacaktır.
>It is True that C# Station provides C# programming language content.
>The statement above is not False.
Asağıdaki tablo tamsayı tiplerini, boyutlarını ve alabilecekleri değer aralıklarını
göstermektedir.
Type (
Tip )
Size (in
bits)(boyut )
Range (aralık)
sbyte 8 -128 to 127
byte 87 0 to 255
short 16 -32768 to 32767
ushort 16 0 to 65535
int 32 -2147483648 to 2147483647
uint 32 0 to 4294967295
long 64 -9223372036854775808 to 9223372036854775807
ulong 64 0 to 18446744073709551615
char 16 0 to 65535
Tamsayı tipleri küsuratsız islemler için çok elverislidirler. Fakat char( karakter) tipi
Unicode standartlarına uygun olarak bir karakteri temsil eder. Yukarıdaki tablodan da
göreceğiniz gibi elimizde çok sayıda tamsayı tipimiz vardır. Bunlardan istedikleriminizi
ihitiyaçlarımıza göre rahatça kullanabiliriz.
Bir sonraki tablo ise ondalık (floating point ) ve decimal veri tiplerini,boyutlarını,
hassasiyetlerini ve geçerli oldukları aralıkları listeler.
Type (
Tip )
Size (in
bits)(boyut )
Precision Range
float 32 7 digits 1.5 x 10-45 to 3.4 x 1038
Double 64 15-16 digits 5.0 x 10-324 to 1.7 x 10308
Decimal 128 28-29 decimal places 1.0 x 10-28 to 7.9 x 1028
Ondalıklı sayıları küsuratlı islemlerde kullanmak iyi olur. Bunun yanında muhasebe ve
finansal islemler için decimal veri tipi daha uygun olacak sekilde tasarlanmıstır.
Bilgisayar programları islemleri yaparken ifadeleri kullanırlar ve sonuç ortaya çıkartırlar.
Programlarda yer alan ifadeler değiskenler ve isleçlerden ( operatör) olusurular. Bir
sonraki tabloda isleçleri, isleçlerin islem sıralarını ve isleme yönlerini görebilirsiniz.
Category (
kategori)
Operator(s) (isleç/isleçler)
Associativity(iseme
yönü)
Primary
(x) x.y f(x) a[x] x++ x-- new typeof sizeof
checked unchecked left Unary + - ! ~ ++x --x
(T)x
left
Multiplicative * / % left
Additive + - left
Shift << >> left
Relational < > <= >= is left
Equality == != right
Logical AND & left
Logical XOR ^ left
Logical OR | left
Conditional
AND
&& left
Conditional
OR
|| left
Conditional ?: right
Assignment = *= /= %= += -= <<= >>= &= ^= |= right
Sol isleme yönü demek islemlerin soldan sağa doğru yapıldığıdır. Sağ isleme yönü
demek islemlerin sağdan sola doğru yapıldığıdır. Mesala atama isleçlerinin hepsinde
önce sağ tarafın sonucu bulunur ve bu sonuç sol tarafa aktarılır.
Kod 2 : Unary Operators: Unary.cs
using System;
class Unary
{
public static void Main()
{
int unary = 0;
int preIncrement;
int preDecrement;
int postIncrement;
int postDecrement;
int positive;
int negative;
sbyte bitNot;
bool logNot;
preIncrement = ++unary;
Console.WriteLine("Pre-Increment: {0}", preIncrement);
preDecrement = --unary;
Console.WriteLine("Pre-Decrement: {0}", preDecrement);
postDecrement = unary--;
Console.WriteLine("Post-Decrement: {0}", postDecrement);
postIncrement = unary++;
Console.WriteLine("Post-Increment: {0}", postIncrement);
Console.WriteLine("Final Value of Unary: {0}", unary);
positive = -postIncrement;
Console.WriteLine("Positive: {0}", positive);
negative = +postIncrement;
Console.WriteLine("Negative: {0}", negative);
logNot = false;
logNot = !logNot;
Console.WriteLine("Logical Not: {0}", logNot);
}
}
Đfadeler islemler yapılırken arka-artırma ve arka-azaltma isleçleri önce değiskenin
değerini döndürür sonra değisken üzerinde artırma veya azaltma islemini yapar. Diğer
taraftan, ön-artırma ve ön-azaltma isleçleri önce değisken üzerinde artırma veya
azaltma islemini yapar sonra değiskenin son halini döndürür.
Kod 2‘de unary (tek) değisken önce sıfır olarak atanıyor. Ön-artırma (pre-increment)
isleci uygulandığında, unary değiskenin değeri 1’e çıkıyor ve “preIncrement”
değiskenine atanıyor. Hemen arkasında Ön-azaltma(pred-decrement) isleci sayesinde
unary değiskenimiz tekrar sıfır değerini alıyor preDecrement değiskenine bu değer
atanıyor.
Arka-azaltma (post-decrement) isleci unary değiskenimize uygularsak bu
değiskenimizin değeri değisiyor ama önce değiskenin ilk değeri postDecrement
değiskenine atanıyor. Sonra ise arka-artırma (post-increment) islecini uygularsak unary
değiskenimizin değeri azalıyor fakat postIncrement değiskenin değeri unary
değiskenimizin ilk değeri olarak kalıyor.
Mantıksal değil isareti, doğru ifadeyi yanlıs, yanlıs ifadeyi ise doğru olarak değistirir.
Kod 2’inin çıktısı söyle olacaktır :
>Pre-Increment: 1
>Pre-Decrement 0
>Post-Decrement: 0
>Post-Increment -1
>Final Value of Unary: 0
>Positive: 1
>Negative: -1
>Logical Not: True
Kod 3. Binary Operators: Binary.cs
using System;
class Binary
{
public static void Main()
{
int x, y, result;
float floatResult;
x = 7; y = 5;
result = x+y;
Console.WriteLine("x+y: {0}", result);
result = x-y;
Console.WriteLine("x-y: {0}", result);
result = x*y;
Console.WriteLine("x*y: {0}", result);
result = x/y;
Console.WriteLine("x/y: {0}", result);
floatResult = (float)x/(float)y;
Console.WriteLine("x/y: {0}", floatResult);
result = x%y;
Console.WriteLine("x%y: {0}", result);
result += x;
Console.WriteLine("result+=x: {0}", result);
}
}
Kod 3: te birçok aritmetik islemler yapılıyor. Bu islemlerin sonucunu da sizler tahmin
edebilirsiniz...
“floatResult” değiskenin tipi ondalıklı sayı tipi olduğu ve “x” ve “y” değiskenlerimiz
tamsayı tiplerinde oldukları onları açık biçimde ondalıklı sayı tipine çevirdik ( explicitly
cast ) ve bu sekilde islem yaptık.
Yukarıdaki kod parçasında bir de kalan (remainder % ) islecinin kullanılısına dair örnek
verdik. Bu isleç, iki sayının bölümününden kalan sayıyı sonuç olarak döndürür.
Son olarak yazdığımız ifadede yeralan atama isleci de (+=) C/C++ ve C#
programcılarının sıklıkla kullandıkları bir atama ve islem yapma türüdür. Bu ifade
aslında su ifadenin kısa yoludur : “result = result + x”.
Simdiye kadar burada sıkça gördüğünüz diğer veri tipi ise string ( karakter dizisi veya
karakter katarı)’dir. String veri tipi Unicode karakter tiplerinin bir listesini içerirler ve
tek çift tırnak isaretleri arasında yazılırlar.
C# Kontrol yapıları ve seçme islemleri
Bu derste C# dilindeki ifadeler,tipler ve değiskenler anlatılacaktır. C# dilinde kullanılan
seçme veya kontrol ifadelerini öğreneceksiniz.
Dersimizin hedefleri :
• "Đf" (eğer) ifadesinin kullanımı "
• "switch" (çoktan-seç) ifadesini kullanımı "
• "break" ifadesinin "switch" ifadesi içerisinde nasıl kullanıldığı
• " "goto" ifadesinin yerinde ve etkili kullanılması
Önceki derslerimizde, her program belirli ifadeleri sırasıyla çalıstırıp bitiyordu. Program
içinde inputlara veya program içinde yapılan hesaplara göre değisik islemler
yapılmıyordu. Bu derste öğrendiklerimiz de programlarımızın belirli sartlara göre değisik
sekillerde çalısmasını sağlayacaktır. Đlk seçme ifademiz "if". "if" kontrol yapısının 3
temel formu vardır.
Kod 1 : IF yapısının değisik formlarda kullanımı : IfSelection.cs
using System;
class IfSelect
{
public static void Main()
{
string myInput;
int myInt;
Console.Write("Please enter a number: ");
myInput = Console.ReadLine();
myInt = Int32.Parse(myInput);
// Single Decision and Action with brackets
if (myInt > 0)
{
Console.WriteLine("Your number {0} is greater than zero.", myInt);
}
// Single Decision and Action without brackets
if (myInt < 0)
Console.WriteLine("Your number {0} is less than zero.", myInt);
// Either/Or Decision
if (myInt != 0)
{
Console.WriteLine("Your number {0} is not equal to zero.", myInt);
}
else
{
Console.WriteLine("Your number {0} is equal to zero.", myInt);
}
// Multiple Case Decision
if (myInt < 0 || myInt == 0)
{
Console.WriteLine("Your number {0} is less than or equal to zero.",
myInt);
}
else if (myInt > 0 && myInt < = 10)
{
Console.WriteLine("Your number {0} is between 1 and 10.", myInt);
}
else if (myInt > 10 && myInt < = 20)
{
Console.WriteLine("Your number {0} is between 11 and 20.", myInt);
}
else if (myInt > 20 && myInt < = 30)
{
Console.WriteLine("Your number {0} is between 21 and 30.", myInt);
}
else
{
Console.WriteLine("Your number {0} is greater than 30.", myInt);
}
}
}
Kod 1'de tüm program boyunca tek bir değiskeni kullanıyoruz, "myInt". Kullanıcıdan
etkilesimli olarak veri almak için önce "Lütfen bir sayı giriniz :" iletisini konsula
yazdırıyoruz. "Console.Readline()" ifadesi ile program kullanıcıdan bir değer girmesini
bekler. Bir rakam yazılınca ve enter tusuna basılınca program yazılan değeri önce string
tipinde olan myInput değiskenine atanıyor. String olarak alınan veriyi program tamsayı
tipinde bir değisken olarak kullanmak istediği için "myInput" tamsayı tipine
dönüstürülmeli. Bu dönüsüm için "Int32.Parse(myInput)" komutunu kullandık ( tip
dönüsümleri ve Int32 gibi veri tipleri ileriki derslerde incelenecektir.) Daha sonra,
dönüsümün sonucu "myInt" isimli değiskene aktarılıyor.
Artık istediğimiz tipte bir veriye sahibiz ve bunu "if" ifadesi içinde isleyebiliriz. "if" ifade
bloğunun ilk formu su sekildedir : if(mantıksal ifade) { "mantıksal ifade"nin doğru olası
durumunda yapılması gerekenler }. Öncelikle "if" anahtar kelimesi ile baslamalıyız.
Sonrası parantezler arasındaki mantıksal ifade. Bu mantıksal ifadenin doğru veya yanlıs
olması bulunur. Biz programımızda kullanıcıdan aldığımız sayının sıfırdan büyük olup
olmadığını kontrol ediyoruz">0" ile. Eğer sayı sıfırdan büyükse, mantıksal ifadenin
sonucu doğrudur ve { } parantezleri arasındaki kod bloğu çalıstırılır. Eğer mantıksal
ifade yanlıs bir sonuç üretirse { } arasındaki kon bloğu çalıstırılmadan bloktan sonraki
ifadelere geçer.
Đkinci "if" ifadesi aslında birincisi ile aynıdır, ikincisi sadece blok içinde değildir. Eğer
boolean ifade doğru sonuç üretirse, bu ifadeden hemen sonraki çalısır. Boolena ifadenin
yanlıs olması durumunda ise bu ifadeden hemen sonraki ifade çalıstırılmadan bir
sonraki ifadeye geçilir ve o ifade çalıstırılır. Bu sekildeki "if" yapısını, boolean ifadenin
doğru olmasında sadece bir tane ifade çalıstırılacaksa yeterlidir. Buna karsın "if"
ifadesinin sonucuna göre birden fazla ifade isleme konulacaksa blok olarak { }
parentezleri arasında yazılır. Benim kisisel önerim "if" den sonra çalıstırılacak ifade
sayısına bakmadan, bu ifade(leri) her durumda blok olarak yazmaktır. Đleride yeni
programın okunmasında ve yeni islevler eklenmesinde size hatalardan kaçmanıza
yardım eder.
Birçok zaman size eğer/değilse türünde çalısacak bir "if" yapısı gerekebilir. Üçüncü tip
"if" ifadesi eğer doğru değer üretirse sunları yap, doğru değilse "else" anahtar
sözcüğünden sonraki kodları çalıstır türünde bir yapısı olarak yazılmalıdır.
Birden fazla mantıksal ifadeyi islememiz gerektiğinde ise, if/else if/else tipinde bir "if"
yapısını kullanmak gerekir. Dördüncü örneğimizde bu tip bir if yapısını görebilirsiniz. Bu
tip yapı yine "if" ve boolean ifadesi ile baslar. Boolean ifade doğru ise hemen alttaki
bloktaki kodlar çalıstırılır. Bunun yanında, boolean ifadenin değisik durumlarına göre
"else if" iç yapısı kullanılır. "else if" de aynı if gibi bir boolean ifadeyi alır ve sonucu
doğru hemen sonraki bloktaki kodları çalıstırır.
Boolean ifadenin alabileceği tüm ihtimallere göre bu sekilde "else if" ifadeleri
sıralanabilir fakat en sonda bir "else" ile yukarıdaki tüm sartların yanlıs olması
durumunda çalıstırılacak kodları belirleriz. Bu dördüncü "if" yapısında da yine sadece bir
tane if/else blok yapısı çalıstırılır.
"switch" yapısı da "if/else if/else" yapısına çok benzer.
Kod 2 : Switch ifadeleri : SwitchSelection.cs
using System;
class SwitchSelect
{
public static void Main()
{
tring myInput;
int myInt;
begin:
Console.Write("Please enter a number between 1 and 3: ");
myInput = Console.ReadLine();
myInt = Int32.Parse(myInput);
// switch with integer type
switch (myInt)
{
case 1:
Console.WriteLine("Your number is {0}.", myInt);
break;
case 2:
Console.WriteLine("Your number is {0}.", myInt);
break;
case 3:
Console.WriteLine("Your number is {0}.", myInt);
break;
default:
Console.WriteLine("Your number {0} is not between 1 and 3.",
myInt);
break;
}
decide:
Console.Write("Type \"continue\" to go on or \"quit\" to stop: ");
myInput = Console.ReadLine();
// switch with string type
switch (myInput)
{
case "continue":
goto begin;
case "quit": Console.WriteLine("Bye."); break;
default:
Console.WriteLine("Your input {0} is incorrect.", myInput);
goto decide;
}
}
}
Kod 2'de birkaç tane "switch" yapısı örneğimiz var. "switch" yapısı yine "switch" anahtar
kelimesi ile baslar ve sınanacak değiskeni parantez içinde belirtiriz. Switch yapısının
çalısması için su veri tiplerinden bir tanesini kullanmak gerekir : sbyte,short,ushort,int,
long, ulong, char, string, or enum ( enum daha sonraki bir derste islenecektir.)
Birinci örneğimizdeki "switch" yapısı int tipinde bir değer almaktadır. Tamsayı
değiskenimizin alabileceği değerlere göre değisik islemler yapabiliriz. "myInt"
değiskenimizin herbir ihtimalini değerlendirirken "case" anahtar kelimesini, muhtemel
değerini ve iki nokta üst üste ":" yapısında bir sınama yapıyoruz. Örneğimizde, "case 1
:", "case 2: ", ve "case 3:" seklinde yazdık. Sınama sonuçlarından uygun olanın hemen
altında kod bloku yer alır. Bu kod blokundan sonra ise "break" veya "goto" ifadelerini
kullanmamız gerekir.
Đsterseniz "default" seçeneğini de "switch" ifadesi ile birlikte kullanabilirsiniz. "default"
ifadesinin altındaki kod bloku, "defult"'tan önceki "case"'lerin hiçbiri sınamayı
geçemediği zaman çalısır ve tüm "case"'lerden sonra gelir.
Her "case" 'den sonra "break" ifadesinin zorunlu olduğunu tekrar hatırlatalım. "break"
ifadesi "switch" yapısından dısarı çıkmayı ve alttaki kodlara geçmemizi sağlar. "default"
anahtar kelimesinin kod blokundan hemen sonra "break" koymak programcının isteğine
kalmıstır. Switch ifadesinde iki tane dikkat edilmesi gereken husus vardır.
Birincisi, farklı durumları (case'leri) ard arda aralarına hiç kod yazmadan sıralamaktır.
Aslında burada yapılan is, değiskenimizin birden fazla değeri için tek bir "case" kod
bloku olusturmaktır. Bir case ve hemen arkasına baska bir case yazdığımızda program
otomatik olarak bir sonraki "case" 'e geçer. Asağıdaki kodu incelediğimizde, "myInt"
değiskeni 1,2, veya 3 değerlerinden herhangi birini alırsa kendi değerini ekrana
yazdırıyoruz. Diğer durumda ise değiskenimizin değerinin 1 ve 3 arasında olmadığını
ekrana yazdırıyoruz.
switch (myInt)
{
case 1:
case 2:
case 3:
Console.WriteLine("Your number is {0}.", myInt);
break;
default:
Console.WriteLine("Your number {0} is not between 1 and 3.", myInt);
break;
}
Kod 2'de yer alan ikinci "switch" yapısı ise "goto" ifadesinin nasıl kullanılacağını
göstermek amacıyla yazılmıstır. "goto" , programın belirli bir kısmında yer alan, özel
etiket (label) ile belirtilmis kısmına atlamasına ve oradan itibaren çalısmaya devam
etmesine yarar. Programımızda kullanıcı "continue" yazarsa "begin" olarak belirlenmis
etikete gider ve oradan çalısmaya devam eder. Aslında bu sekilde "goto" kullanmak
etkili bir döngü olur. Eğer kullanıcı "end" yazarsa program "bye" yazar ve döngüden
programımız çıkar.
Açıkça görülüyor ki "goto" kelimesini kullanmak bize programda belirli sartlar altında
güç kazandırır. Yine de "goto" ifadesini programda sık bir sekilde kullanmak "sipagetti"
kod olarak adlandırılan programlamaya yol açabilir ki, bu tür kodlama programı hem
okurken hem de hataları ayıklarken büyük sorunlara sebep olabilir.
C# ile Temel Windows Formları Olusturma
Bu yazımızda Windows programlamanın temel elemanlarından olan windows formlarının
nasıl olusturulduğunu ve nasıl kullanıldığını göreceğiz, windows formlarını açıklarken basit
bir dört islem yapan hesap makinası olusturacağız.Windows formları derken neyi
kastediyoruz? Textbox, label, button gibi önemli elemanların hepsi birer windows
formudur. Bu windows formlarına ulasmak için System.Windows.Forms isimalanını
kullanıyoruz. Ve tabi ki programımızın aktif bir windows uygulaması olarak çalısması için
de asağıdaki isimalanlarını projemize ekliyoruz.
using System.Drawing;
using System.Collections;
using System.ComponentModel;
Programımızı yazmaya baslamadan önce programımızın kodlarını içerecek bir isim alanı
olusturalım. Ben buna CsHesapMakinasi adi verdim.(Makaleyi okurken kaynak kodu
incelemenizi tavsiye ederim) Siz istediğiniz baska bir isim kullanabilirsiniz.
Daha önceki makalelerimizde belirttiğimiz gibi programımızın çalısması için derleyiciye
programın baslangıç noktasını bildirmemiz gerekirdi.Bu baslangıç noktası da genelde
main() fonksiyonu oluyordu. Kodumuzu öyle ayarlayacağız ki main() fonksiyonu icra
edildiğinde çalıstırmak istediğimiz windows formu ekranda görünsün. Bunun için main
içine asağıdaki kodu yazıyoruz.
Application.Run(new Form1()); // Yeni bir Form1 nesnesi olusturularak uygulama olarak
baslatılıyor.
Su an için Form1 hakkında en ufak bir bilgiye sahip değiliz.Peki bu Form1 nasıl
olusturulacak. Yukarıda da bahsettiğimiz gibi Form1 sınıfından bir nesne olusturmak için
System.Windows.Forms isimalanını kullanmalıyız. Bu yüzden bu isim alanının altında
bulunan Form sınıfından yeni bir Form1 sınıfı türetmemiz gerekecek, bu türetme islemi
asağıdaki gibidir. Form1 sınıfını türettikten sonra Form1' içinde bulunacak elemanları
tanımlıyoruz.4 islemi yapmak için 4 buton, isleme giren değerler için 2 textbox ve 2 tane
de label formu tanımlıyoruz.Tanımlama islemi asağıdaki gibidir.
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Button button3;
private System.Windows.Forms.Button button4;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox deger1;
private System.Windows.Forms.TextBox deger2;
private System.Windows.Forms.TextBox sonuc;
private System.Windows.Forms.Label isaret;
}
Simdi bu windows formunun ekrana nasıl basıldığını inceleyelim.Main() islevi içinde yeni
bir Form1 nesnesi yaratıldığında Form1 nesnesine ait kurucu islev olan Form1() islevi
çağrılır.(Kaynak kodu inceleyin). Form1() kurucu islevinde ise InitializeComponent(); adlı
bir fonksiyon çağırılarak Form1 nesnesine ait olan üye elemanlarla (button,label,textbox
vs) ilgili ilk islemler yapılır.From1 açıldığı zaman Form1 içinde bulunan elemanlarla iligili
yapmak istediğimiz ilk özellikleri InitializeComponent() fonksiyonu içinde yapıyoruz.
private void InitializeComponent()
{
// form1 içinde yer alacak elemanlar yaratılıyor(kaynak kodu inceleyin)
this.deger2 = new System.Windows.Forms.TextBox();
this.sonuc = new System.Windows.Forms.TextBox();
this.button2 = new System.Windows.Forms.Button();
this.isaret = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// elemanlarla ilgili baslangıç özellikleri veriliyor.
//
this.deger1.Location = new System.Drawing.Point(16, 8); //deger1 adlı textbox
için yer bildirimi
this.deger1.Name = "deger1";
this.deger1.TabIndex = 0;
this.deger1.Text = "0";
this.name="Form1"; //Form1 nesnesinin kendisi için this anahtar sözcüğünü
kullanıyoruz
this.text = "Hesap Makinası ";
this.BackColor = System.Drawing.Color.FromArgb(((System.Byte)(255)),
((System.Byte)(128)), ((System.Byte)(0)));
this.ClientSize = new System.Drawing.Size(400, 149);
}
Simdi sıra elemanlarla ilgili olayların birbirleri ile iliskisine.Mesela bir buton formunun click
olayının form tarafından yakalanabilmesi için asağıdaki satırları yazmalıyız.
this.button4.Click += new System.EventHandler(this.button4_Click);
private void button4_Click(object sender, System.EventArgs e)
{
isaret.Text="/";
sonuc.Text=System.Convert.ToString(System.Convert.ToInt32(deger1.Text)/
System.Convert.ToInt32(deger2.Text));
}
buton4_Click() islevinde, çalısma zamanında bir nesnenin özelliklerinin nasıl
değistirildiğini görüyoruz.button4 bölme islemi yaptığından isaret.Text="/"; yazdık. sonuc
adlı textbox formunun Text özelliği bir string ifadesi olduğu için islemlerimizi yaptıktan
sonra sonuc.text ifadesine atama yapabilmek için System.Convert isimalanında bulunan
ToString islevini kullanarak ifadeyi String türüne dönüstürüyoruz. Aynı sekilde String
olarak aldığımız türler için aritmetik islem yapabilmek için yine aynı isimalanında bulunan
ToInt32 islevi ile String türünü int32 formatına dönüstürüyoruz. Bütün bu islemleri 4
butonumuz için yaptığımızda dört islem yapabilen basit ve bol bol bug içeren (unutmayın
amacımız sadece formların kullanımını öğrenmek) bir hesap makinamız olacak.
.NET Teknolojilerine Giris
Günümüzde bilgisayar dünyasında internet olmazsa olmaz derecede önemli bir yer
edinmeye basladı. Artık insanlar ev ve isyerlerinde kullandıkları uygulamalarına da
internet üzerinden erisip kullanmak istiyorlar. Bu internetin getirdiği özgürlüğün
kaçınılmaz bir sonucudur.Peki yazılım dünyası buna hazırmıydı? Gelistirilen her programı
kolayca internet ormanında da çalıstırabilirmiydik? Bu soruların cevapları bir sene
öncesine kadar hayır, olamaz veya su andaki sistemler bu denli özgürlüğü bize
sağlamıyor türündendi.
Microsoft'un ASP'si ile veya PHP ile yapılan uygulamalar tam olarak insanların isteklerine
cevap veremiyordu. Her ne kadar iyi ve gelismis web uygulamalarını bir yere kadar
yapabiliyorduksa da belirli bir noktadan sonra C++,Delphi veya VB ile gelistirdiğimiz
modülleri web uygulamamıza ekleyerek sorunlarımızı halletmeye çalısyorduk. Tabi bu tür
yöntemler programın gelisme süresini uzatıyordu. Zamanın giderek önem kazandığı bir
devirde haliyle programlarımızı da hızlı bir sekilde gelistirmemiz gerekiyor(du). Hızlı
uygulama gelistirme(Rapid Application Development- RAD) geleneksel programlama
araçlarıyla ve prgramcının yetenekleriyle çözüm bulunacak bir mesele değil. Artık
programlama dilleri, dille birlikte gelen kütüphaneler ve bunlar hakkındaki
dokümantasyonları ile birlikte değerlendiriliyor.
.NET ile birlikte programcının hizmetine sunulan 3400'den fazla sınıf, modern anlamda
çok güzel bir gelistirme ortamı sunuyor. Bu sayede programlamları daha hızlı bir sekilde
gelistirme imkanına sahip bulunuyoruz. .NET kullanarak yazdığımız ASP.NET, Windows
Forms veya mobil cihazlar için gelistirdiğimiz bir uygulamayı birinden diğerine
dönüstürmek isi çok kolay bir sekilde yapılabiliniyor. Bu sayede aynı anda hem windows
hem de web uygulamaları gelistirmek çok hosunuza gidecektir :-).
.NET framework'unun bize sunduğu diğer güzel bir özellik ise platform bağımsızlığıdır.
Artık yazdığınız Windows uygulamaları sadece Windows yüklü sistemlerde değil, .NET
framework'unun kurulu olduğu tüm platformlarda çalısabilecektir. Her ne kadar simdilik
bu alt yapının sadece Windows versiyonuna sahip olsak da Linux grupları tarafından bu
alt yapının Linux versiyonunu çıkartma yönündeki çabalar uzun bir süredir devam
etmektedir.
Peki bunca hos özellikleri bize sağlayan .NET alt yapısında program yazarken hangi dili
veya dilleri kullanmak zorundayız? Bu konuda Microsoft çok radikal bir karar alarak
gelecek için hazırlanmıs yeni alt yapıda Common Language Runtime (CLR) ile uyumlu her
.NET dilini kullanmamıza olanak sağlıyor. .NET ile gelen SDK'da C#,VB.NET ve Js.NET
kullanarak program yazabiliyoruz. Diğer taraftan 30'un üzerinde programlama diliyle
.NET uygulaması gelistirebilirsiniz.
CLR denen sey tam olarak nedir? .NET altyapısında programların çalısmasını kontrol eden
ve isletim sistemi ile programımız arasında yer alan arabirimdir. Normalde yazdığımız
programlar derlenirken makine diline çevrilirdi ve program bu sekilde isletim sistemi ile
direkt bağlantı kurarak çalısırdı. Fakat platform bağımsız bir gelistirme ve yürütme ortamı
istiyorsanız ne olacak? Đste tam bu anda CLR devreye girer ve .NET programlarını farklı
platformlarda makineye ve isletim sistemine göre programımızı çalıstırır. Normalde bir
Windows, Linux veya MACOS kurulu sistemler aynı programın kodunu çalıstıramazlar. Bu
platformlar için programın ayrı ayrı yazılıp, onlara göre hazırlanmıs derleyicilerde
derlenmesi gerekir. Dünyada çok sayıda yaygın platform olduğunu düsünürsek, bunların
herbiri için ayrı ayrı derleme islemini tek bir isletim sisteminde yapmamız imkansız
gibidir. Bu durumda çözüm , ortak bir aradil kullanmak ve herbir platform için bu aradile
çevrilmis programın kodunu çalıstıracak altyapıları hazırlamaktır.
Simdi su soruya sıra geldi: "Đyi de .NET hangi aradili kullanıyor?" Sorumuzun cevabı
MSIL(Microsoft intermediate Language) .NET platfomunda hangi dili kullanırsak
kullanalım yazdığımız programın kodu direkt olarak makine diline değil de MSIL'e çevrilir.
Bu sadece programı çalıstırdığımız sistemde kurulu olan CLR çalısma anında MSIL
kodlarını çevirerek programımızı çalıstırır, çalısma anında derleme islemini ise JIT
derleyicileri (Just in Time compilers) üstlenir.
Gelecek makalemizde JIT'ler, MSIL language, CTS (Common Type System) gibi daha
teknik konuları detaylı olarak ele almayı düsünüyorum. Sizlere kolaylıklar dilerim.
.NET'in CLR, CTS ve JIT derleyicileri
Önceki yazımızda "dot NET" platformu konusuna giris yapmıstık.(Yazıyı okumak için
tıklayın) Burada ise daha detaylı olarak .NET kavramlarını inceleyeceğiz ve .NET'le
Java'nın karsılastırıldığı bir testin sonuçlarına yer vereceğiz.
.NET platformunda istediğimiz programlama dili ile program yazabileceğimizi önceki
yazımızda söylemistik. Bunun için tek sart, kullandığımız dilin .NET için yazılmıs olan bir
derleyicisine ihtiyacımız olduğudur. .NET uyumlu programlama dili olustururken belirli
standartlara uyulması gerekir. Bu standartlar CLS (Common Language Specifications -
Dillerin ortak özellikleri) ile belirlenmistir. CTS(Common Type System) ise veri tipleri,
nesneler, arayüzler ve programlama dillerine ait özellikleri tanımlar ve CLS'in bir parçası
olarak karsımıza çıkar. CLS'de tanımlanmıs kurallara uymak sartı ile istersek kendi
programlama dilimizi dahi gelistirebiliriz veya herhangi bir dili .NET platformunda
uygulama gelistirmek üzere değistirebiliriz.
CLR ,programlarımızı değisik sekilde derleyebilir. Varsayılan derleme türü JIT(Just IN
TIME- çalısam anında derleme) 'dır. Program çalısırken daha önce derlenmemis bir
parçasına gelince hemen o kısmı da derler ve bunu hafızda chach'e koyar. Tekrar aynı
program parçasını çalıstırmak gerekirse burayı hafızadan çalıstırır. Eğer RAM 'imizi yeteri
kadar büyükse, programın tamamı derlenmis ve hafızada depolanmıs durumda olabilir.
Bu durumda programımız çok hızlı çalısır.
Hafızamızın yeteri kadar büyük olmadığı durumlarda EconoJIT (Ekonomik JIT)
derleyicisini kullanabiliriz. Bu derleyici ile programın derlenmis kısımları hafızada
depolanmaz ve her seferinde aynı program parçası derlenir. Tabi ki bu derleyici normal
JIT'e göre programlarımızı daha yavas çalıstırır. Ama RAM 'imizi çok daha az kullanır.
CLR ile gelen üçüncü derleyicimiz PreJIT(ön JIT derleyicisi) ise derleme isini program
çalısmadan önce yapar ve tüm makine kodlarını bir yerde saklar. Çalısma anında çok hızlı
olan programımız diğer JIT derleyicileriyle derlenmis olanlara nazaran çok hızlı çalısır.
Kolayca görebileceğimiz birkaç noktaya da parmak basmak istiyorum. .NET ile yazdığınız
programlar diğerlerine göre yavas çalısır. Çünkü iki defa derleme asamasından geçerler,
program kodu MSIL'ye, MSIL ise makine koduna çevrilir. Diğer taraftan .NET ile
programlarımız platform bağımsız olacak, .NET uyumlu herhangi bir dil ile program
gelistirebileceğiz ve programımız CLR altında daha güvenli bir sekilde çalısacaktır.
.NET perfromans testi linkindeki sonuçlara göre : Genelde C# Java'dan 3.30 kat daha
hızlı. C# Visual C++ 6.0'dan ise 3.11 kat daha hızlı çalısıyor. Hatta VB.NET kodu VB 6.0'a
nazaran 46.45 kat daha hızlı çalısıyor. :-)
Visual C# ile Programlamaya Giris
Visual C#, Visual Studio ailesinin yeni üyesidir, bu yeni dil c ve c++ temelleri üzerine
kurulmasına rağmen komponent temelli programlama tekniğini gelistirmek için birtakım
yeni özellikler eklenmistir. C# dilinin sentaksı C ve C++ programcılarına çok tanıdık
gelecektir. Bundan süpheniz olmasın.
Genel Açıklamalar
Bu yazıda göreceğimiz basit uygulamada QuickSort algoritmasını kullanarak nasıl basit bir
C# projesinin olusturulduğunu göreceğiz. Bu uygulamada bir c# programında en sık
kullanılan yapılardan olan dosyaya ve console ekranına okuma/yazma, fonksiyon
olusturma ve basit dizilerin kullanımı açıklanacaktır.
Bu yazı kesinlikle C# dilinin tüm özelliklerinin anlatıldığı yazı değildir. Bu yazı C# dilini
öğrenmek için sizlere bir baslangıç noktası sunar.
Önerilen Kaynaklar
Visual Studio.NET (Beta 2 veya sonrası) örnek kaynak kodu derlemeniz için gereklidir.
C/C++ bilgisi size bu yazıyı anlamanızda yardımcı olabilir ama gerekli değildir.
Adım 1. Projeye Baslama
Visual Studio ile program gelistirme organizasyonu solution(çözüm) çalısma alanları
üzerindendir. Solution dediğimiz ortam bir veya daha fazla projeyi içerebilir. Bu makale
için tek bir C# projesi içeren bir solution olusturacağız.
Yeni bir proje olusturmak
1. Visual Studio.NET ortamından, File | New | Project menülerini seçin.
2. Soldan(Project Types) Visual C#, sağdan(Templates) ise Console Application
butonlarını seçin.
3. Projenizin adını belirleyin ve projenizin hangi klasörde saklanacağını belirleyin.Bu
klasör Visual Studio tarafından otomatik olusturulur.Proje adı olarak ben quicksort
yazıyorum.Siz istediğiniz adı verebilirsiniz.
4. OK tusuna basalım ve yola koyulalım.
"Visual C# Solution" ortamımız
Visual Studio.NET içinde bir Visual C# projesi bulunan bir solution olusturdu. Proje
assemblyinfo.cs ve class1.cs adlı iki tane dosya içermektedir.
Bundan sonraki adımlarda projemizi nasıl derleyeceğimizi ve bu iki dosya hakkında
detaylı bilgiyi öğreneceğiz.
Adım 2. Hello, World!
Kusura bakmayın ama geleneği bozmadan ilk defa C programlama dili ile yazılmıs olan
"Hello, World!" programını c# ile yazacağız. Bu bir gelenektir ve her yeni bir dili
öğrenmeye basladığınızda bunu siz de göreceksiniz.
Kaynak Kodu Değistirme
1. Solution Explorer 'da bulunan 'class1.cs'dosyasına çift tıklayın. Solution Explorer 'ı
göremiyorsanız, view menüsünü kullanarak görünür hale getirebilirsiniz.
2. Sablonla olusturulmus koda asağıda kırmızı ile yazılmıs kısmı ekleyin (class1.cs).
using System;
namespace quicksort
{
///
/// Summary description for Class1.
///
class Class1
{
static void Main(string[] args)
{
//
// TODO: Add code to start application here
//
Console.WriteLine ("Hello, C#.NET World!");
}
}
}
3. Dikkat edin siz kodunuzu yazdıkça Visual Studio size sınıflar ve fonksiyon adları
hakkında bilgi verir, çünkü .NET Framework tip bilgisini yayınlamaktadır.
Uygulamamızı Derleyelim
1. Programımızda değisiklik yaptığımıza göre artık Build menüsünden Build 'ı seçerek
programımızı derleyebiliriz.
2. Hata ve mesajlar en altta bulunan "Output Window" denilen pencerede görünür.
Eğer herhangi bir hata yoksa uygulamamızı Debug menüsü altında bulunan 'Start
without Debugging' menüsüne tıklayarak çalıstırabiliriz.
Programımızın Çıktısı
Asağıda programımızın Visual C# içinden çalıstırılarak olusturulmus çıktısının ekran
görüntüsü mevcuttur.
Değisiklikleri Anlamak
System.Console sınıfına ait WriteLine() fonksiyonu kendisine argüman olarak gönderilen
dizgeyi sonuna satır sonu karakteri de ekleyerek ekrana yazar.
Bu fonksiyon integer ve floating-point gibi diğer veri tiplerini de argüman olarak alabilir.
Program belleğe yüklendiğinde programın kontrolu Main() fonksiyonuna gelir.WriteLine()
fonksiyonunu oraya yazmamızın sebebi budur.
Adım 3. Programın Yapısı
Simdi basit bir Hello World uygulaması gelistirmis olduk, simdi de bir Visual C#
uygulamasının basit componentlerini inceleyelim.
Kaynak Kod Yorumları
// karakterlerinden sonra gelen ve satırın sonuna kadar olan sözcükler yorum satırlarıdır
ve C# derleyicisi tarafından görünmezler. Aynı zamanda birden fazla satıra yorum
eklemek istiyorsak /* */ karakterleri arasına yorum yazarız.
// Bu satır derleyici tarafından görülmez
/* Aynı zamanda bu blok da
derleyici tarafından görünmez*/
Using Komutu
.NET Framework gelistiricilere yüzlerce yararlı sınıflar sunar. Mesela, Console sınıfı,
console ekranına ait girdi ve çıktıları isler. Bu sınıflar hiyerarsik bir ağaç içinde organize
edilmistir. Aslında Console sınıfının tam ismi System.Console ' dur. Diğer sınıflar ise
System.IO.FileStream ve System.Collections.Queue. içindedirler.
using komutu bize sınıfın ismini namespace(isim alanı) kullanmadan kullanabilmemizi
sağlar.
Asağıda kırmızı ile yazılan yazılar using komutunun uygulamasının sonucudur.
using System;
class Class1
{
static void Main(string[] args)
{
System.Console.WriteLine ("Hello, C#.NET World!");
Console.WriteLine ("Hello, C#.NET World!");
}
}
Sınıf Bildirimi
C++ ve Visual Basic 'den farklı olarak C# 'da bütün fonksiyonlar bir sınıf içerisinde
olmalıdır. C# 'da bir sınıf tanımlamak için class anahtar sözcüğü kullanılır.Bu durumda
bizim uygulamamızda, Class1 sınıfı Main() adında bir fonksiyon içerir. Eğer sınıf bildirimini
bir isim alanı blokları içine alırsak sınıflarımızı CSharp.QuickSortApp seklinde bir hiyerarsi
içine alabiliriz.
Sınıflar hakkında çok derin bir bilgi vermeyi düsünmüyorum.Fakat bizim örneğimizin
neden bir parçası olduğunu açıklamakta fayda gördüm.
Main() Fonksiyonu
Program belleğe yüklendiğinde Main() fonksiyonu programın kontrolünü eline alır, bu
yüzden baslangıç kodlarımızı daima Main() fonksiyonu içinde yazmalıyız. Komut satırı
argümanları ise bir string dizisi olan args dizisine aktarılır.(mesela: delete x ) Burada
programımızın adı delete ise x bir komut satırı argümanıdır.
Adım 4. Console Girisi
Simdi islemlerimize bir QuickSrot uygulaması gelistirerek devam edelim. Đlk yapmamız
gereken kullanıcya hedef ve kaynak dosyasının isimlerini sormak olacaktır.
Source Code Modifications
1. class1.cs dosyasına asağıda kırmızı ile yazılanları yazın. Burada sınıf ismi ve
isimalanı ismi bizim için çok önemli değildir.
// namespaces ekleme
using System;
// namespace tanımlama
namespace MsdnAA
{
// uygulama sınıfı tanımlama
class QuickSortApp
{
// uygulama olusturma
static void Main (string[] szArgs)
{
// Programın hakkında
Console.WriteLine ("QuickSort C#.NET Sample Application\n");
// kullanıcıdan bilgi alma
Console.Write ("Source: ");
string szSrcFile = Console.ReadLine ();
Console.Write ("Output: ");
string szDestFile = Console.ReadLine ();
}
}
}
Console'dan Okuma
Console sınıfının ReadLine() metodu kullanıcıya bir giris ekranı sunar ve geriye
kullanıcının girdiği dizgeyi(string) geri döndürür.Bu metod bellek tahsisatını otomatik
olarak kendi içinde yapmaktadır ve .NET garbage collector mekanizması sayesinde iade
etmeniz gereken herhangi bir alan yoktur.
Program Çıktısı
Programı Debug | Start Without Debugging menülerini kullanarak çalıstırın.
Asağıda programımızın son halinin çıktısını görüyorsunuz.
Adım 5. Dizilerin Kullanımı
Programımız sıralama yapmadan önce giristen satırları alarak bir dizi içinde saklaması
gerekir. Simdi .NET temel sınıflarından olan ArrayList sınıfını inceleyeceğiz.
Kaynak Kod Değisikliği
1. class1.cs dosyası içinde asağıda kırmızı ile gösterilen yerleri ekleyin.
// isim alanı ekleme
using System;
using System.Collections;
// isimalanı tanımlama
namespace c#nedircom
{
// uygulama sınıfı tanımlama
class QuickSortApp
{
// uygulama baslangıcı
static void Main (string[] szArgs)
{
Console.WriteLine ("QuickSort C#.NET Sample Application\n");
// Dosya isimlerini almak için komut yaz
Console.Write ("Source: ");
string szSrcFile = Console.ReadLine ();
Console.Write ("Output: ");
string szDestFile = Console.ReadLine ();
// TODO: Read contents of source file
ArrayList szContents = new ArrayList ();
}
}
}
ArrayList sınıfının kullanımı
ArrayList sınıfına direkt ulasabilmek için System.Collections isimalanını projemize using
komutuyla ekliyoruz. Bu sınıf dinamik olarak büyüyüp küçülebilen nesne dizileri için
kullanılır. Yeni bir eleman eklemek için basit bir sekilde Add() metodunu kullanabilirsiniz.
Bu diziye eklenen yeni eleman orijinal nesne için referans olarak kullanılır, ve garbage
collector alan iadesi için hazır olacaktır.
string szElement = "insert-me";
ArrayList szArray = new ArrayList ();
szArray.Add (szElement);
Dizinin var olan bir elemanına ulasabilmek için, diziye ait Item() metoduna ulasmak
istediğimiz elemanın sıra(index) numarasını geçebiliriz.Kısaca [] operatörlerini kullanarak
da istediğimiz elemana Item() metodunu kullanmadan da ulasabiliriz.
Console.WriteLine (szArray[2]);
Console.WriteLine (szArray.Item (2));
ArrayList sınıfının daha birçok metodu vardır, ancak biz bu uygulamada sadece ekleme ve
okuma yapacağız. ArrayList sınıfına ait tüm metod ve özellikleri öğrenmek için MSDN
kitaplığına basvurabilirsiniz.
Adım 6. File Girdi/Çıktı(Input/Output)
Simdi isterseniz dosyadan okuma ve dosyaya yazma islemlerini gerçeklestirelim.
Dosyadaki her satırı okuyup, bir string dizisinin her elemanını bir satır gelecek sekilde
ekleyeceğiz.Sonraki asamada ise QuickSort algoritmasını kullanarak diziyi sıralı bir
sekilde yazacağız.
Kaynak Kod Değisikliği
1. class1.cs dosyasına asağıda kırmızı ile yazılanları yazın. Burada sınıf ismi ve
isimalanı ismi bizim için çok önemli değildir.
// isimalanı ekleme
using System;
using System.Collections;
using System.IO;
namespace MsdnAA
{
class QuickSortApp
{
static void Main (string[] szArgs)
{
string szSrcLine;
ArrayList szContents = new ArrayList ();
FileStream fsInput = new FileStream (szSrcFile,
FileMode.Open,FileAccess.Read);
StreamReader srInput = new StreamReader (fsInput);
while ((szSrcLine = srInput.ReadLine ()) != null)
{
// dizinin sonuna ekleme
szContents.Add (szSrcLine);
}
srInput.Close ();
fsInput.Close ();
// TODO: Buraya QuickSort fonksiyonu gelecek
// sıraları satırları yazma
FileStream fsOutput = new FileStream (szDestFile,FileMode.Create,
FileAccess.Write);
StreamWriter srOutput = new StreamWriter (fsOutput);
for (int nIndex = 0; nIndex < szContents.Count; nIndex++)
{
// hedef dosyaya satır yazma
srOutput.WriteLine (szContents[nIndex]);
}
srOutput.Close ();
fsOutput.Close ();
// basarıyı kullanıcıya bildirme
Console.WriteLine ("\nThe sorted lines have been written.\n\n");
}
}
}
Kaynak Dosyadan Okuma
Kaynak dosyayı açmak için FileStream sınıfını ve StreamReader sınıfını kullanarak
ReadLine() metodu ile dosyayı okuyoruz. ReadLine() metodunu dosyanın sonunu ifade
eden NULL değerine geri dönene kadar çağırıyoruz.Döngü içinde okunan satırları dizinin
içine ekliyoruz ve sonrada tüm nesneleri kapatıyoruz.
Hedef Dosyaya Yazma
Dosyaya yazarken dizinin sıralanmıs olduğunu varsaydık ve öyle devam ettik. Aynı
seklide FileStream nesnesini ve StreamWriter sınıfını kullanarak hedef dosyaya yazma
islemini yaptık.
Adım 7. Fonksiyon Yaratma
Son asamada diziyi QuickSort algoritması ile sıralayan fonksiyonu yazmaya geldi. Bu
fonksiyonu uygulamamızın ana sınıfının içine koyacağız.
Kaynak Kod Değisikliği
1. class1.cs dosyasına asağıda kırmızı ile yazılanları yazın. Burada sınıf ismi ve
isimalanı ismi bizim için çok önemli değildir.
// isim alanı ekleme
using System;
using System.Collections;
using System.IO;
namespace c#nedircom
{
class QuickSortApp
{
static void Main (string[] szArgs)
{
... ... ...
// QuickSort fonksiyonuna parametrelerin geçisi
QuickSort (szContents, 0, szContents.Count - 1);
... ... ...
}
// QuickSort fonksiyonu
static void QuickSort (ArrayList szArray, int nLower, int nUpper)
{
if (nLower < nUpper)
{
// Ayırma ve sıralama islemi
int nSplit = Partition (szArray, nLower, nUpper);
QuickSort (szArray, nLower, nSplit - 1);
QuickSort (szArray, nSplit + 1, nUpper);
}
}
// QuickSort bölmelere ayırma
static int Partition (ArrayList szArray, int nLower, int nUpper)
{
// Đlk elemanı bulma
int nLeft = nLower + 1;
string szPivot = (string) szArray[nLower];
int nRight = nUpper;
// Dizi elemanlarını bölme
string szSwap;
while (nLeft <= nRight)
{
while (nLeft <= nRight)
{
if (((string) szArray[nLeft]).CompareTo (szPivot) > 0)
break;
nLeft = nLeft + 1;
}
while (nLeft <= nRight)
{
if (((string) szArray[nRight]).CompareTo (szPivot) <= 0)
break;
nRight = nRight - 1;
}
// Eğer uygunsa yer değistirme islemi yap
if (nLeft < nRight)
{
szSwap = (string) szArray[nLeft];
szArray[nLeft] = szArray[nRight];
szArray[nRight] = szSwap;
nLeft = nLeft + 1;
nRight = nRight - 1;
}
}
szSwap = (string) szArray[nLower];
szArray[nLower] = szArray[nRight];
szArray[nRight] = szSwap;
return nRight;
}
}
}
QuickSort() Fonksiyonu
Bu fonksiyon alt sınır, üst sınır ve bir dizi olmak üzere 3 tane parametre almaktadır.
QuickSort fonksiyonu Partition() fonksiyonunu çağırarak diziyi iki parçaya ayırır. Bu
parçaların birinde belirlenen bir diziden önceki ,diğer parçasında ise sonraki elemanlar
bulunur. Sonra fonksiyon tekrar kendini çağırarak bu iki parçanın sıralanmasını sağlar.
Yukarıdaki kodlarda yeni gördüğümüz metod CompareTo() metodudur. Bu metod iki
string ifadesini karsılastırır.
QuickSort Uygulamasını Çalıstırma
Bu adım uygulamamızın son adımı olacaktır.Simdi programımızı derleyip çalıstırabiliriz.
Programımızın çalısması için bir text dosyası olusturmamız sıralama yapabilmek için
gerekecek. Exe dosyasının bulunduğu dizini text dosyasını olusturup programı
çalıstıralım.
Programın Çıktısı
Asağıda programımızı çalıstırdıktan sonraki ekran çıktısı mevcuttur. Yukarıdaki ekranda
ise output.txt dosyasında meydana gelen değisiklikleri görebilirsiniz.
Adım 8. Debugger Kullanmak
Debugger aracı programımızın problemlerini çözmek için önemli ve gerekli bir araçtır. Đyi
bir baslangıç yaptığımıza göre son asamada programımız içinde nasıl dolasacağımıza ve
QuickWatch' ı nasıl kullanacağımıza bakalım.
Breakpoint' leri Ayarlama
Program Debugger içinde çalısırken eğer breakpoint olan bir noktaya gelinirse program
sonlandırılır ve debugger control 'ü bizim elimize geçer. Herhangi bir satıra breakpoint
koymak için ilgili satıra sağ tıklayıp asağıdaki gibi Insert BreakPoint menüsüne tıklayın.
BreakPoint bulunan satırlar kırmızı ile gösterilir. BreakPoint ' i kaldırmak için ilgili satıra
sağ tıklayıp Remove BreakPoint menüsünü seçin.
Program içinde Adım Adım ilerlemek
Asağıdaki sekilde görüldüğü gibi bir breakpoint olusturduktan sonra debugger ile
programımızı çalıstıralım. Debug menüsünden daha önceden de yaptığımızı gibi Start
Without Debugging yerine Start menüsüne tıklayın. Bu sekilde programı çalıstırdığımızda
program debugger ile çalısmaya baslayacaktır, ve breakpoint 'ler aktif olmaya
baslayacaktır.
Program breakpoint 'in olduğu noktaya geldiğinde, debugger programın kontrolünü alır.
Asağıdaki sekilde görüldüğü gibi sarı okla programın geldiği nokta gösterilir.
Kod içinde adım adım ilerlemek için menü çubuğundan Debug | Step Over ' ı seçerek sarı
okun hareketini izleyin. Debug | Step Into komutu gitmek istediğimiz fonksiyona
gitmemizi sağlar. Asağıda iki defa Step Over menüsünü seçtikten sonra kaynak
kodumuzun görüntüsü mevcuttur.
Eğer programımızın diğer bir sonlandırıcı nedene (yeni bir breakpoint, exception , exit,
debug) gelene kadar devam etmesini istiyorsanız Debug | Continue menülerini seçin.
Değisken Değerlerini Takip Etme
Debugger 'ın kontrolü bizde iken farenin imlecini kaynak kodda istediğimiz değisken
üzerine götürerek değiskenin o anki değerini öğrenebiliriz.
Aynı zamanda bir değiskene sağ tıklayarak QuickWatch menüsünü açabilirsiniz.
QuickWatch ile değisken hakkında daha detaylı bilgiler elde edebilirsiniz. Mesela diziler
hakkında asağıdaki sekilde görüldüğü gibi bilgiler elde edilebilir.
Diğer Debugger Araçları
Visual Studio yukarıda bahsettiğimiz araçları dısında farklı araçlarda barındırır. Mesela
Call Stack Wiever aracı ile o an hangi fonksiyonun çağrıldığı ile ilgili bilgiyi elde
edebilirsiniz. Bellekteki değerleri ve bir prosesteki thread 'leri de aynı anda
görebileceğimiz araçlar mevcuttur.
Son Söz & Sonuç
Bu yazının amacı sizlere Visual Studio ile nasıl basit bir uygulamanın yapılacağını adım
adım göstermekti. Diğer .NET ve C# kaynaklarını arastırmak için sizi
cesaretlendirebildiysek ne mutlu bize. Bu yazıyı buraya kadar okuduğunuza göre en
azından su asamada çalısan bir projeniz var, istediğiniz gibi üzerinde değisiklikler yapıp
sonucu görebilirsiniz.
C# ile Client/Server ve Socket programlamaya giris
Bu makalemizde TCP protokolüyle basit bir Client/Server programı yapacağız, C# ile
socket programlama yapabilmek için System.Net.Sockets isimalanı altında bulunan
sınıfları kullanacağız. Yapacağımız programda server bir console uygulaması, client ise
windows formlarını kullanarak yapacağımız windows uygulaması olacak.Amacımız basit
bir Client/Server çatisi kurmak olduğu için uygulamamız çok basit olacaktır. Siz yazının
tamamını dikkatlice incelediğinizde ve yaratıcılığınızı kullandığınızda çok daha gelismis
uygulamalar yapabilirsiniz. Belki bir sunucu tabanlı script dili bile gelistirebilirsiniz :).
simdi yazacağımız programda kullanıcı Windows uygulaması vasıtası ile server olan
programımıza bağlanacak. Form üzerinde bulunan butona tıkladığımızda yine form
üzerinde bulunan textbox girisindeki yazıyı server programımız alacak ve yazıda kaç
karakter olduğunu client programına gönderecek.Client program ise bir mesaj kutusu ile
kullanıcıya bildirecek.Öncelikle client olan kullanıcıdan mesajın geldiğini düsünürek Server
programımızı yazalım. Server programımızı yazmaya baslamadan önce programda Soket
programlama için kullandığımız sınıflara ve onların üye fonksiyonlarına kullandığımız
kadarıyla bir göz atalım.
:::: TcpListener Sınıfı(System.Net.Sockets) ::::
TcpListener sınıfı TCP protokolü ile çalısan servislere bağlanmamızı sağlar. Mesela HTTP
ve FTP protokolleri TCP servislerini kullanırlar. TcpListener sınıfının kurucu fonksiyonunu 3
değisik sekilde çağırabiliriz.
1- )IPEndPoint sınıfını kullanarak IP numarası ve port numarası içeren bir bilgiyi kullanma
yolu ile
2- )IP adresi ve port numarasını geçerek çağırma
3- )Sadece Port numarası ile çağırma.Bu durumda varsayılan ağ arayüzünüz TCP
servislerini sağlayacaktır.
Biz bu programda 3. sıktaki gibi bir kullanımı tercih ettik.
public void Start();
TcpListener sınıfına ait bu metod network servislerinden ilgili port'u dinleyerek verileri
almaya baslamamızı sağlar.
public Socket AcceptSocket();
TcpListener sınıfına ait bu metod veri transferi için geri dönüs değeri olarak bir Socket
nesnesi döndürür.Bu geri dönen socket ilgili makinanın IP adresi ve port numarası ile
kurulur.(kurucu islev ile)
:::: Socket Sınıfı(System.Net.Sockets) ::::
Socket Sınıfı ile ilgili asağıdaki örneği inceleyelim
Socket s = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,ProtocolType.Tcp );
Socket sınıfının bu kurucu islevi parametre olarak AdressFamily dedigimiz adresleme
semasi Soket tipi ve kullanacagimiz protokol tipini alir. Bu 3 paremtere de .NET
Framework class kütüphanesinde Enum sabitleri olarak tanimlanmistir.
Programimizda yazdigimiz "Socket IstemciSoketi = TcpDinleyicisi.AcceptSocket();" satiri
ile geri dönen soket nesnesinde bu 3 parametrede tanimlanmistir.
public bool Connected();
Bu metod ile Soketin baglanip baglanmadigini geri dönen bool degeri ile anliyoruz.Eger
soket hedef kaynaga bagliysa true degilse false degerine geri döner.
:::: NetworkStream Sinifi(System.Net.Sockets) ::::
NetworkStream sinifi kurucularindan olan "void NetworkStream(Socket x);" fonksiyonu
ilgili kendisine gönderilen soket nesnesine ait datalari NetworkStream türünden nesnede
tutar.bu programda kullandigimiz soket tipi stream oldugu için bu sinifi kullaniyoruz.
NetworkStream sinifi içinde islem yapabilmemeiz için ise System.IO isiamalaninda
bulunan StreamReader ve StreamWriter siniflarini kullanacagiz.
Ön bilgileri aldığımıza göre server programımızı yazalım. Asağıdaki ilk kaynak kod
server.cs dir. Satır aralarına size yardımcı olabilecek yorumlar ekledim.Kaynak dosyayı
özellikle makaleme dosya olarak eklemiyorumki siz asağıdaki kodları tek tek yazıp daha
iyi öğrenin.
:::: TcpClient Sinifi(System.Net.Sockets) ::::
Tcp servislerine bağlantı sağlamak için TcPClient sınıfı kullanılır. Istemci programımızda
TcpClient sınıfının <public TcpClient(string, int);> kurucu islevini kullanıyoruz. Đlk
parametre bilgisayar adı ikincisi ise port numarasıdır.
public NetworkStream GetStream();
Bu metod ile belirtilen port tan gelen veriler bir NetworkStream nesnesine aktarılır.
GetStream metodunun geri dönüs değeri NetworkStream olduğu için atama islemini
NetworkStream türünden bir nesneye yapmamız gerekir.
Not: Yesil ile yazılan satırlar yorum satırlarıdır.Html formatında bir alt satıra inmis olan
yorum satırlarını copy&paste ile programınıza aktarırken o satırları tekrar tek satır haline
getirmeyi unutmayın, aksi halde programınız derlenemez.
//Server.cs
using System; // bunu her zaman eklememiz lazim
using System.IO ; //StreamReader ve StreamWriter siniflari için
using System.Net.Sockets; // Socket, TcpListener ve NetworkStrem siniflari için
public class Server
{
public static void Main()
{
//Bilgi alisverisi için bilgi almak istedigimiz port numarasini TcpListener sinifi ile
gerçeklestiriyoruz
TcpListener TcpDinleyicisi = new TcpListener(1234);
TcpDinleyicisi.Start();
Console.WriteLine("Sunucu baslatildi...") ;
//Soket baglantimizi yapiyoruz.Bunu TcpListener sinifinin AcceptSocket metodu ile
yaptigimiza dikkat edin
Socket IstemciSoketi = TcpDinleyicisi.AcceptSocket();
// Baglantının olup olmadığını kontrol ediyoruz
if (!IstemciSoketi.Connected)
{
Console.WriteLine("Sunucu baslatilamiyor...") ;
}
else
{
//Sonsuz döngü sayesinde AgAkimini sürekli okuyoruz
while(true)
{
Console.WriteLine("Istemci baglantisi saglandi...");
//IstemciSoketi verilerini NetworkStream sinifi türünden nesneye aktariyoruz.
NetworkStream AgAkimi = new NetworkStream(IstemciSoketi);
//Soketteki bilgilerle islem yapabilmek için StreamReader ve StreamWriter
siniflarini kullaniyoruz
StreamWriter AkimYazici = new StreamWriter(AgAkimi);
StreamReader AkimOkuyucu = new StreamReader(AgAkimi);
//StreamReader ile String veri tipine aktarma islemi önceden bir hata olursa bunu
handle etmek gerek
try
{
string IstemciString = AkimOkuyucu.ReadLine();
Console.WriteLine("Gelen Bilgi:" + IstemciString);
//Istemciden gelen bilginin uzunlugu hesaplaniyor
int uzunluk = IstemciString.Length;
//AgAkimina, AkimYazını ile IstemciString inin uzunluğunu yazıyoruz
AkimYazici.WriteLine(uzunluk.ToString());
AkimYazici.Flush() ;
}
catch
{
Console.WriteLine("Sunucu kapatiliyor...");
return ;
}
}
}
IstemciSoketi.Close();
Console.WriteLine("Sunucu Kapatiliyor...");
}
}
Đste buda Istemci programımız. Öncelikle sunu belirtiyimki asağıdaki kodların çoğunu
Visual C# kendiliğinden hazırladı, o yüzden size Tavsiyem Visual C# kullanmanız.
Öncelikle asağıdaki sekilde gördüğünüz form yapısını benzer bir form hazırlayın.Sonra da
buton_click metodunu form_kapatma metodunu ve using ifadelerini ekleyin. Yada
zamanınız çoksa asağıdaki kodları teker teker yazın. (Đsteyene kaynak kodu da
gönderebilirim)
//client.cs
using System;
using System.Net.Sockets;
using System.IO ;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
public class Form1 : System.Windows.Forms.Form
{
//Burda server da tanımladıklarımızdan farklı olarak TcpClient sınıfı ile serverdan gelen
bilgileri alıyoruz
public TcpClient Istemci;
private NetworkStream AgAkimi;
private StreamReader AkimOkuyucu;
private StreamWriter AkimYazici;
private System.Windows.Forms.Button buton;
private System.Windows.Forms.TextBox textbox;
private System.ComponentModel.Container components = null;
public Form1()
{
InitializeComponent();
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
private void InitializeComponent()
{
//Bu satırları Visual C# olusturdu.
this.buton = new System.Windows.Forms.Button();
this.textbox = new System.Windows.Forms.TextBox();
this.SuspendLayout();
this.buton.Location = new System.Drawing.Point(8, 40);
this.buton.Name = "buton";
this.buton.Size = new System.Drawing.Size(248, 23);
this.buton.TabIndex = 0;
this.buton.Text = "Sunucuya Baglan";
this.buton.Click += new System.EventHandler(this.buton_Click);
this.textbox.Location = new System.Drawing.Point(8, 8);
this.textbox.Name = "textbox";
this.textbox.Size = new System.Drawing.Size(248, 20);
this.textbox.TabIndex = 1;
this.textbox.Text = "Buraya Sunucuya göndereceginiz yaziyi yazin";
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(264, 69);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.textbox,
this.buton});
this.MaximizeBox = false;
this.Name = "Form1";
this.Text = "C#nedir?com";
this.Closing += new
System.ComponentModel.CancelEventHandler(this.form1_kapatma);
this.Load += new System.EventHandler(this.Form1_Load);
this.ResumeLayout(false);
}
//giris noktamız olan mainde yeni bir form1 nesnesini çalıstırıyoruz
static void Main()
{
Application.Run(new Form1());
}
//From1 yüklendiğinde TcpClient nesnesi olusturup AgAkımından(NetworkStream) verileri
okuyoruz
private void Form1_Load(object sender, System.EventArgs e)
{
try
{
Istemci = new TcpClient("localhost", 1234);
}
catch
{
Console.WriteLine("Baglanamadi");
return;
}
//Server programında yaptıklarımızı burda da yapıyoruz.
AgAkimi = Istemci.GetStream();
AkimOkuyucu = new StreamReader(AgAkimi);
AkimYazici = new StreamWriter(AgAkimi);
}
}
private void buton_Click(object sender, System.EventArgs e)
{
//Kullanıcı butona her tıkladığında textbox'ta yazı yoksa uyarı veriyoruz
//Sonra AkimYazici vasıtası ile AgAkımına veriyi gönderip sunucudan gelen
//cevabı AkimOkuyucu ile alıp Mesaj la kullanıcıya gösteriyoruz
//Tabi olası hatalara karsı, Sunucuya bağlanmada hata olustu mesajı veriyoruz.
try
{
if (textbox.Text=="")
{
MessageBox.Show("Lütfen bir yazi giriniz","Uyari");
textbox.Focus();
return ;
}
string yazi;
AkimYazici.WriteLine(textbox.Text);
AkimYazici.Flush();
yazi = AkimOkuyucu.ReadLine();
MessageBox.Show(yazi,"Sunucudan Mesaj var");
}
catch
{
MessageBox.Show("Sunucuya baglanmada hata oldu...");
}
}
//TVe bütün olusturduğumuz nesneleri form kapatıldığında kapatıyoruz.
public void form1_kapatma(object o , CancelEventArgs ec)
{
try
{
AkimYazici.Close();
AkimOkuyucu.Close();
AgAkimi.Close();
}
catch
{
MessageBox.Show("Düzgün kapatilamiyor");
}
}
Asağıdaki server ve client programlarımızın aynı anda çalıstıkları sırada alınmıs ekran
görüntüleri mevcuttur.
Yazı hakkında sorularınızı bana sorabilirsiniz. Kaynak kodları özel bir istek geldiğinde
buraya koyabilirim.
C#'ta Temel Metin Dosyası Đslemleri
Micssoft NET ile programcıların hizmetine sunulan hazır sınıf kütuphaneleri sayesinde
diğer dillerde programcıları uğrastıran birçok konu üzerinde program yazmak artık bir
zevk haline geldi. Girdi/Çıktı (I/O) islemleri de böyle zevkli hale gelen konulardan biridir.
Biz bu yazımıda metin dosyası (text file) ile ilgli temel birkaç islem üzerinde duracağız.
Metin dosylarını olusturmak, yazmak, içeriklerini okumak için System isimuzayında
bulunan Text alt uzayındaki sınıfları kullanıyoruz. Asağıdaki programımızda 3 tane
metodumuz var. Birincisi, DosyayaYaz() metin dosyasını olusturup bu dosya ya birkaç
sey yazdırıyor. Bu metod önce StreamWriter sınıfından dosya isimli bir obje olusturuyor.
Daha sonra StreamWriter sınıfında bulunan WriteLine() metodu ile 2 satır yazıyoruz
dosyamıza. Son olarak dosyamızı dosya.Close() ile kapatıyoruz.
Đkinci metodumuz, DosyadanOku(), ise bir metin dosyasının içeriğini ekrana yazdırıyor.
Bunun için önce StreamReader sınıfına ait dosyaOku nesnemizi olusturuyoruz. Sonra
dosyamızı dosyaOku=File.OpenText(dosyaIsmi); komutu ile açıyoruz. Dosyamızın ilk
satırında bulunan yazıyı "yazi" isimli değiskenimize yazi=dosyaOku.ReadLine(); ile
aktarıyoruz. Bundan sonra ise eğer okuduğumuz satırda yazı varsa (yani dosyanın sonu
değilse) o satırı ekrana yazdırıp bir sonraki satırı okuyoruz. Okuma ve ekrana yazdırma
islemlerini dosyanın sonuna kadar (yada okuduğumu satırın içeriğinin null olana kadar )
devam ediyoruz. Son olarak ise dosyaOku.Close() ile dosyamızı kapatıyoruz.
Üçüncü ve son fonksiyonumuz ise metin dosyamızın sonuna birseyler ekleyen
DosyayaEkle()'dir. Yine StreamWriter sınıfından dosya isimini verdğimiz bir nesne
olusturuyoruz. Dosyamızı File.AppenText() metodu ile açıyoruz ki bu metod sayesinde
dosyanın sonuna istedğiğmiz veriyi kolayca ekleyebiliriz. dosya.WriteLine("Bu da en son
Append ile eklediğimiz satır..."); komutu ile tırnaklar arasında metni dosyamızın sonuna
ekliyoruz. Her zamanki gibi açtığımız dosyayı isimiz bitince hemen dosya.Close(); ile
kapatıyoruz.
Asağıdaki programı sisteminizde deerkeyi çalıstırmanızı ve hatta kod ile oynayıp
değisiklikleri incelemenizde yarar olduğunu düsünüyorum. Herkese basarılar...
using System;
using System.IO;
using System.Text;
class TextFile
{
public static void Main(string[] args)
{
// Metin dosyamıza birseyler yazan fonksiyon..
DosyayaYaz();
// Metin dosyamızı okuyan ve ekrana yazan fonksiyon
DosyadanOku("c:\\Deneme.txt");
// Metin dosyamızın sonuna birseyler ekleyen fonksiyon
DosyayaEkle("c:\\Deneme.txt");
Console.ReadLine();
}
static void DosyayaYaz()
{
//StreamWriter classından dosya isimli bir nesne olusturalım
StreamWriter dosya = new StreamWriter("c:\\Deneme.txt");
//Dosyamıza birinci satırı yazalım
dosya.WriteLine("Metin dosyamızın ilk satırı");
//Buda dosyamıza yazdığımız ikinci satır
dosya.WriteLine("Đkinci satır...");
//Dosyamızın kapatılım..
dosya.Close();
//Yazma islemini basarı ile tamamladığımızı kullanıcıya bildirelim..
Console.WriteLine("Dosya yazımı Basarı ile tamamlandı...");
}
static void DosyadanOku(string dosyaIsmi)
{
// Text dosyasından okuyan StreamReader sınıfına ait bir
// dosyaOku nesnesini olusturuyoruz
StreamReader dosyaOku;
// dosyadan okuyacağımız yazıyı string olarak depolamak için
// yazı nesnemizi olusturuyoruz.
string yazi;
//Dosyamızı okumak için açıyoruz..
dosyaOku=File.OpenText(dosyaIsmi);
//Dosyamızı okumak için açıyoruz ve ilk satırını okuyoruz..
yazi=dosyaOku.ReadLine();
/* okuduğumuz satırı ekrana bastırıp bir sonraki satıra geçiyoruz
* Eğer sonraki satırda da yazı varsa onu da okuyup ekrana bastırıyoruz.
* Bu islemleri dosyanın sonuna kadar devam ettiriyoruz.. */
while(yazi!=null)
{
Console.WriteLine(yazi);
yazi=dosyaOku.ReadLine();
}
// dosyamızı kapatıyoruz..
dosyaOku.Close();
}
static void DosyayaEkle(string dosyaIsmi)
{
//StreamWriter classından dosya isimli bir nesne olusturalım
StreamWriter dosya;
// dosyamızın sonuna birseyler eklememek için açıyoruz..
dosya=File.AppendText(dosyaIsmi);
// dosyanın sonuna birsey ekliyoruz..
dosya.WriteLine("Bu da en son Append ile eklediğimiz satır...");
// Dosyamızı kapatıyoruz..
dosya.Close();
Console.WriteLine("Dosyanın sonuna basarı ile ekledik...");
}
}
C# Dilinde Yapılandırıcılara Asırı Yüklenmesi
C# dilinde bulunan ve nesne yönelimli programlama kavramı içinde yeralan güzel bir
özelliği yapılandırıcılara asırı yüklenme konusunu bu yazımızda inceleyelim. Bazen bir
nesneyi olustururken bu isi birden farklı sekillerde yapmak zorunda kalırız.
Mesela elimizde bir programcı sınıfı var ve bu sınıftan olusturduğumuz her programcı
nesnesinin tüm özelliklerini olustururken veremeyebiliriz. Programcı nesnelerimizi
olustururken bir kısım bilgileri sonradan elde etmek durumda kalabiliriz. Veya baska bir
programda herhangi bir sınıfın bir örneğini olustururken bu nesneye ait olan n tane
özelliğin bir kısmına ihitiyaç duymadan girilmeyen parametrelere varsayılan değerler
atayarak isimizi halletme sansımız da var.
Asağıdaki programımızda, Programci sınıfımızın yasi, adi, soyadi ve kullandigiDil
olmak üzere 4 tane özelliği bulunmakta. Sınıfımızın 4 tane yapılandırıcısı var. Bunların
isimleri aynı (zaten yapılandırıcının ismi sınıf ismi ile aynı olur). Farklı olan ise aldıkları
parametre sayıları ve tipleri olabilir.
Sırasıyla, birinci yapılandırıcı hiç bir değer almıyor. Đkinci yapılandırıcımız iki tane
değisken alıyor. Bunlardan yas ve adi değerleridir. Üçüncüsü ise yas, adi ve soyadi
parametrelerini alarak nesnemizi olusturuyor. Son yapılandırıcımız ise yas, adi, soyadi
ve kullandigiDil değiskenleridir. Đlk üç yapılandırıcı alınmayan yas değiskenine 0
diğerlerine null değerleri atıyor.
Sınıfımızın besinci ve son metodu ise bu sınıftan ürettiğimiz bir nesnenin özelliklerini
ekrana yazdırıyor. Eğer nesnenin bir özelliğinin değeri varsa onu yazdırıyor, yoksa bu
özellik hakkında bir malumatımız yok gibisinden bir uyarı yazıyor ekrana.
Pogramımızın Main() fonksiyonu içinde önce 4 tane (a, b, c, ve d) programcı nesnesi
olusturuyoruz. Bunların herbirini ayrı yapılandırıcılar ile olusturuyoruz. showOzellik()
metodumuz ile bunların herbirinin özelliklerini ekrana yazdırıyoruz.
using System;
class OverLoadedFunctions
{
static void Main(string[] args)
{
Programci a = new Programci();
Programci b = new Programci(23,"Ziya");
Programci c = new Programci(27,"Kamuran","Kamiloğlu");
Programci d= new Programci(30,"Hayrettin","Kütükçü","C#");
a.showOzellikler();
b.showOzellikler();
c.showOzellikler();
d.showOzellikler();
Console.ReadLine();
}
}
class Programci
{
int yasi;
string adi;
string soyadi;
string kullandigiDil;
// Hic parametre almayan bir yapılandırıcı..
public Programci()
{
this.adi=null;
this.yasi=0;
this.soyadi=null;
this.kullandigiDil=null;
}
// Đsmini ve yasini alan bir yapılandırıcı..
public Programci(int yasi, string adi)
{
this.adi=adi;
this.yasi=yasi;
this.soyadi=null;
this.kullandigiDil=null;
}
// Đsmini, soyismini ve yasini alan bir yapılandırıcı..
public Programci(int yasi, string adi, string soyadi)
{
this.adi=adi;
this.yasi=yasi;
this.soyadi=soyadi;
this.kullandigiDil=null;
}
// Đsmini, soyismini kullandığı dili ve yasini alan bir yapılandırıcı..
public Programci(int yasi, string adi, string soyadi, string kullandigiDil)
{
this.adi=adi;
this.yasi=yasi;
this.soyadi=soyadi;
this.kullandigiDil=kullandigiDil;
}
public void showOzellikler()
{
Console.WriteLine("***************************************************
**************\n");
if(this.yasi!=0)
Console.WriteLine("Yasi : "+this.yasi);
else Console.WriteLine("Yasi bilgisi elimizde yok su anda...");
if(this.adi!=null)
Console.WriteLine("Adı : "+this.adi);
else Console.WriteLine("Adi bilgisi elimizde yok su anda...");
if(this.soyadi!=null)
Console.WriteLine("Soyadı : "+this.soyadi);
else Console.WriteLine("Soyadı bilgisi elimizde yok su anda...");
if(this.kullandigiDil!=null)
Console.WriteLine("Kullandığı Programlama dili : "+this.kullandigiDil);
else Console.WriteLine("Hangi Dili kullanığını bilmiyoruz be... :-(\n");
Console.WriteLine("\n***************************************************
**************\n");
}
}
C# Đsim uzayları (namespace) Hakkında
Bu yazımızda C# dilindeki namespace ler hakkında genis bir bilgi edineceğiz. Bildiğiniz
gibi programlama dillerinde, programcıların islerini kolaylastırmak için bir takım hazır
kütüphaneler mevcuttur, bu kütüphanelerden bazıları standart olmakla birlikte bazıları
programcılar tarafından sonradan gelistirlmis ve kullanıcların hizmetine sunulmustur.
Mesela MFC ve ATL gibi kütühanelerin kendilerine has amaçları vardır, MFC kütüphanesi
ile bir takım hazır C++ sınıflarına ulasarak temelde zor olan bir takım Windows
platformuna özgü islemleri (forms, dialog box vs.) yapabiliriz. Bu da MFC programcılarına
çalısır bir uygulama yapmak için daha az zaman harcatır. Bu tür kütüphaneler Visul Basic
te ve Java dilinde de vardır. Fakat bu dillerin aksine C# dili ile gelen hazır bir takım sınıf
kütüphaneleri bulunmamaktadır, kısacası standart bir C# kütüphanesi mevcut değildir.
Bu demek değildir C# ile isimiz daha zor olacak, aslında daha kolay, .NET Framework
dediğimiz altyapının bize veya diğer programlama dillerini kullanan programcılara
sunduğu bir takım temel türler ve sınıflar mevcuttur. Bütün bu sınıfları ve türleri binary
düzeyde iyi oırganize edebilmek için .NET, namespace kavramını sıklıkla kullanmaktadır.
Demekki .NET teki sınıf kütüphaneleri bir dilden bağımsız bir yapıdadır. MFC gibi sadece
C++ için yada baska bir dil için gelistirilmemistir. Çok normal olarak Visual Basic.NET
kullanıcısı ile C# kullanıcısı aynı kütüphaneden faydalanırlar.
Namespace ler .NET Framework sınıf kütüphanesindeki veri türlerini ve sınıfları
kullanabilmemiz için C# dilinde using anahtar sözcüğü ile birlikte kullanılır ve derleyiciye
bildirilir. Diğer dillerde ise bu isimalanları farklı sekilde derleyiciye bildirilir, ama temelde
yapılan is .NET Framework sınıf kütüphaneslerini kullanma hakkı almaktır. Asağıda C#,
Visual Basic ve Managed C++ ile yazılmıs 3 farklı ama aynı isi yapan 3 program
görüyorsunuz. Programları dikkatlice incelediğinizde namespace lerin sadece eklenme
biçimi ve namespace lerde ki sınıfların sentaks olarak kullanımı farklı. Bize sunudğu
arayüzler ise özel durumlar dısında tamamen aynıdır.
[C#]
using System;
public class C#nedir
{
public static void Main()
{
Console.WriteLine ("Merhaba, beni C# ile yazdılar.")
}
}
[VB.NET]
Imports System
Public Module C#nedir
sub Main()
Console.WriteLine ("Merhaba, beni VB.NET ile yazdılar.")
End Sub
[Managed C++]
using namespace System;
public static void Main()
{
Console::WriteLine ("Merhaba, beni managed C++ ile yazdılar.")
}
Yukarıdaki programlarda gördüğünüz gibi .NET platformunu destekleyen bütün diller aynı
sınıfı kullanarak ekrana yazı yazdırıyorlar.Bu sınıf System isimalanı içinde bulunan
Console sınıfına ait bir fonksiyonla gerçeklestirilmektedir.
Namespace leri kendi yazdığımız kodların organizasyonu içinde kullanabiliriz. Hem böyle
tür isimlerinin karısmasınıda önlemis oluruz, zira bir tür ismi yada sınıf ancak kendi
isimalanı içinde görünürlüğe(visibility) sahiptir. Mesela System isimalanını eklemeden
Console sınıfını kullanamayız. Aynı sekilde kendi yazıdğımız sınıfları için de isimalanları
tanımlayarak, kaynak kodumuzu istediğimiz bir sekilde organize edebiliriz. .NET
Framework sınıf kütüphanesi hiyerarsik bir yapıya sahip olduğu için içeiçe isimalanları
tanımlanmıstır.
Đsimalanlarının kullanımına bir örnek verecek olursak : Diyelimki 2D (iki boyutlu)
grafikleri içeren bir sınıf kütüphanesi gelistiriyoruz, ve bu sınıf kütüphanesi içinde "Nokta"
adlı bir sınıfımız var. Bu isimalanını tanımlamak için namespace anahtar söcüğünün
asağıdaki gibi bir kullanımı vardır.
namespace 2DGraph
{
public class Nokta
{
......
}
}
Yukarıdaki Nokta sınıfını kullanabilmek için programımıza using deyimi ile isimalanını
eklememiz gerekir.Bu islem asağıdaki gibi yapılır.
using 2DGraph;
Nihayet 2DGraph isimli sınıf kütüphanesini olusturduk ve baskalarının kullanımına
sunduk. Bi süre sonra da 3DGraph isimalanı adı altında 3 boyutlu grafik islemleri yapan
yeni bir sınıf kütüphanesi gelistirdik ve te tekrar programcıların hizmetine sunduk. Yine
aynı sekilde 3 boyutlu noktayı temsil etmek için Nokta sınıfımız olsun
namespace 3DGraph
{
public class Nokta
{
......
}
}
Simdi 2DGrap ve 3DGraph sınıf kütüphanelerinin herikisini birden kullanmak isteyen bir
programcı using ile isimalnlarını ekledilten sonra Nokta türünden bir nesne olusturmak
istediğinde derleyici bunun 2D Nokta mı yoksa 3D Nokta mı olduğunu nerden bilecek.
Bunu çözmenin iki yolu vardır. Birincisi veri tipi belirlerken asağıdaki sekildeki bir
kullanım tercih edilir.
Veri türlerinin bu sekilde belirtilmesi pek tercih edilmeyen bir yöntemdir. Çünkü içiçe bir
çok isimalanının tanımlandığı durumlarda kaynak kodumuz gereksiz yere isimalanlarını
yazmakla uzamaktadır. Bu hem okunabilirliği bozmakta hemde programcıya zaman
kaybettirmektedir.
using System;
using 3DGraph;
using 2DGraph;
public class C#nedir
{
public static void Main()
{
3DGraph.Nokta 3dnokta = new 3DGraph.Nokta();
2DGraph.Nokta 2dnokta = new 2DGraph.Nokta();
}
}
Đkinci bir yöntem ise isimalanlarında bulunan sınıflar için takma isim (alias)
kullanmaktır.Bu sayede isimalanlarını bir kez eklediketen sonra o isim alanında bulunan
sınıflara doğrudan erisebiliriz. Bir isim alanındaki sınıfa takma ad asağıdaki sekilde verilir.
using System;
using 3DGraph;
using 2DGraph;
public class C#nedir
{
using Nokta2D= 2DGraph.Nokta;
using Nokta3D = 3DGraph.Nokta;
public static void Main()
{
Nokta2D 2dnokta = new Nokta2d();
Nokta3D 3dnokta = new Nokta3d();
}
}
Yukarıda mavi yazı ile berlirtilen yerlerde takma isimler tanımlanmıstır. Takma isimler
ancak ve ancak tanımlanadıkları blok içinde geçerlidir. Baska bloklarda takma adları
kullanmak derleme zamanında hataya yol açar.
Sonuç : Đsimalanları component(program parçacığı) yazmanın en önemli parçasıdır. Bir
"Merhaba Dünya" programı için isimalanı belitmek sizde takdir edersinizki pek anlam
tasımamaktadır. Đsimalanları daha çok kodumuzun tekrar kullılabilirliğini artırmak için
gelistirilen sınıf kütüphanelerinde kullanılırlar.
C# Dilindeki Temel Veri Türleri
Her dilde olduğu gibi C# dilinde de önceden tanımlanmıs ve dillerin temelini olusturan
veri saklamak için kullanılan bir takım veri tipleri vardır. Bu makalemizde C# dilinde
kullanılan veri türlerine değineceğiz. C# dilinde temel olarak veri tipleri ikiye ayrılır,
bunlar önceden tanımlanmıs veri türleri ve kullanıcı tarafından tanımlanmıs veri türleridir.
Önceden tanımlanmıs veri türleri de kendi arasında referans tipi(reference types) ve
değer tipi(value type) olmak üzere ikiye ayrılır. Bu detaylı bilgileri vermeden önce veri
tipleri nasıl tanımlanır, veri türlerine nasıl ilk değer verilir ve veri türlerinin faaliyet alanı
gibi temel konulardan bahsetmek istiyorum.
Değisken Kavramı
Değiskenler bir programlama dilinde temel verileri saklamak ve bu verileri sonradan
kullanmak için kullanılan bellek bölgeleridir. C# dilinde genel olarak bir değisken
tanımlaması asağıdaki gibi olmaktadır.
Veritipi veriadı ;
Örneğin C# dilinde isaretsiz ve 32 bitlik veriyi temsil eden "a" isimli bir değisken
asağıdaki gibi tanımlanır.
int a ;
Fakat yukarıdaki tanımlamada bir sorun var. "a" adlı değiskende herhangi bir değer
tutulmaktadır.Bu yüzden C# derleyicisi simdilik "a" değiskenini kullanmamıza müsade
etmez, çünkü "a" da neyin olduğu henüz belli değildir.Bu yüzden değiskenlere =(esittir)
operatörüyle ilk değerler atarız, ya da değisken tanımlamasından sonra, değiskene bir
değer atarız. Bir değiskene bir değer atamak için iki farklı yöntem kullanılır. Asağıda bu
iki yönteme ait örnek bulunmaktadır.
int a = 10 ; //değisken tanımlanırken bellekteki değer 10 olarak düzenleniyor.
--------------------
int b;
b = 10 ; /*değisken tanımlandıktan sonra değiskene değer atanıyor.Đslevsel olarak bu
iki kullanım açısından bir fark yoktur.*/
--------------------
int a=10, b;
b = 10 ; /*eğer bir satırda birden fazla değiseken tanımlaması yapmak istiyorsak bu
yapıyı kulanırız.Bu durumda a ve b int türden değiskenlerdir denir.*/
Önemli Not: C# dilinde bir değiskene herhangi bir değer atamadan onu kullanmak
yasaktır. Yani derleme islemi gerçeklesmez, örneğin asağıdaki gibi bir kullanım derleme
zamanında hata verecektir. Bu yüzden eğer bir değiskeni kullanmak istiyorsak yukarıda
açıkladığımız gibi değiskenlere bir değer vermek zorundayız. Bu kural önceden
tanımlanmıs referans tipleri için de değer tipleri için de geçerlidir.
int a ;
Console.WriteLine(a); //Bu ifadeleri içeren bir kod derlenemez.
Değiskenlerin Faaliyet Alanları (Scope)
C# dilinde programın genel akısı açılan ve kapanan parantezler içerisinde yazılır. Bu
açılan ve kapanan parantezler arasındaki bölgeye blok denir. Tanımlanan bir değiskene,
ancak tanımlandığı blok içerisinde ulasılabilir. Örneğin asağıdaki kısa örnekte tanımlanan
örnekte her iki "a" değiskeni birbirinden bağımsızdır ve bellekte ayrı bölgelerde
saklanırlar.
public class deneme
{
public static void Main()
{
{ //Birinci blok
int a=20 ;
}
{//Đkinci blok
int a=20 ;
}
}
}
Yukarıdaki örnekte birinci ve ikinci blokta tanımlanan "a" isimli değiskenler Main bloğu
içinde geçersizdir. Birinci a değiskeninin faaliyet alanı 1.Blok ,ikinci a değiskenin faaliyet
alanı ise 2. Bloktur. Bu durumda Main() bloğunda Console.WriteLine(a); gibi bir ifade
hatalıdır, çünkü Main bloğu içinde tanımlanan bir a değiskeni yoktur. Unutmamalıyız ki
daha sonraki makalelerde detaylı olarak göreceğimiz for ve diğer döngüler de birer blok
olduğu için bu bloklarda tanımlanan değiskenler döngü bloğunun dısında geçersiz
olacaktır. Diğer bir önemli nokta ise faaliyet alanı devam eden bir değiskenin bir daha
tanımlanmasının hataya yol açmasıdır. Örneğin asağıdaki gibi bir durum derleme
zamanında hata verecektir. Çünkü bir değiskenin faaliyet alanı bitmeden aynı isimli
değisken tekrar tanımlanıyor.
public class C#nedir?com
{
public static void Main()
{
int a;
{
int a=20 ;
}
}
}
Gördüğünüz gibi Main bloğunda tanımlanan a değiskeninin faaliyet alanı açılan blokta
devam etmektedir.Bu yüzden yukarıdaki gibi ifadeler geçersidir.Üst seviyede açılan
bloklar alt seviyedeki blokları kapsadığı için, birinci tanımlanan a değiskeni sonradan
açılan blok içinde hala geçerlidir.
Yukarıda anlatılan duruma ters düsüyor gibi görünse de asağıdaki gibi bir kullanım son
derece legal bir durumdur. Bu konuyu daha sonraki makalelerimizde detaylı bir sekilde
inceleyeceğiz.
public class C#nedir?com
{
static int a = 10;
public static void Main()
{
int a;
{
int a = 10 ;
}
}
}
Bu konu sınıflarla ilgili bir konu olduğu için detaylarına girmeyeceğiz ama simdilik böyle
bir kullanımın geçerli olduğunu bilmenizde fayda var.
Sabitler
Bir program boyunca değerinin değismeyeceğini düsündüğümüz verileri sabit veriler
olarak tanımlarız. Sabit veriler tanımlamak için tanımlama satırında const anahtar
sözcüğünü kullanırız. const olarak tanımlanmıs değiskenlerin en büyük avantajı program
içinde sıkça kullandığımız değerleri aniden değistirmek gerektiğinde görülür.Mesela
matematiksel islemler yapan bir programda pi sayısını const olarak tanımlayıp istediğimiz
zaman pi sayısını değistirebiliriz. Tabi bu islemi const değilde normal bir değiskenle de
yapabilirdik, ama su da bir gerçek ki çok uzun programlarda sabit olmasını istediğimiz
değiskeni yanlıslıkla değistirebiliriz. Fakat const olarak tanımladığımız bir değiskenin
değerini değistirmeye çalıstığımızda c# derleyicisi derleme asamasında hata verecektir.
Bu da gözden kaçan bazı hata durumlarını minimuma indirmek demektir. Sabit
ifadeleriyle ilgili bilmemiz gereken 3 önemli kural vardır. Bunlar sunlardır :
1-) Sabitler tanımlandıklarında değerleri atanmaladır. Đlk değer verilmeyen değiskenler
const yani sabit olamazlar.
2-) Sabit ifadelere ancak sabit ifadelerle ilk değer atanabilir yani su sekildeki bir kullanım
hatalıdır. const int = a + b ;
3-) Sabit ifadeleri içsel tasarım olarak zaten statik oldukları için, ayrıca statik olarak
belirtmek hatalıdır ve kullanılamaz.(statik değiskenler ileriki yazılarda detayl olarak
anlatılacaktır.)
Basit bir sabit tanımlaması asağıdaki gibi yapılmaktadır.
const double pi = 3.14 ; // double, kesirli sayıları tutmak için tanımlanmıs bir veri
türüdür.
Değer(value) ve referans(reference) tipleri
C# dilinde önceden tanımlanmıs(c# dilinde varolan tipler) veri tipleri değer tipleri ve
referans tipleri olmak üzere ikiye ayrılır. Bu iki veri tipi arasındaki farkı çok iyi kavramak
gerekir. Daha önce dediğimiz gibi değiskenler bellekte bulunan verilerdir. Aslında bir
değiskeni kullanırken o değiskenin bellekte bulunduğu adresteki veriye ulasıyoruz. Değer
tipleri değiskenin değerini direkt bellek bölgesinden alırlar. Referans tipleri ise baska bir
nesneye referans olarak kullanılırlar. Yani referans tipleri aslında bir çesit bellek bölgesi
olan heap alanında yaratılan veri türlerinin (bunlara kısaca nesne de diyebiliriz)
adreslerini saklarlar. Değer tipleri yaratıldıklarında stack dediğimiz bellek bölgelerinde
olusturulurlar, referans tipleri ise kullanımı biraz daha sınırlı olan heap dediğimiz bellek
bölgesinde saklanırlar. C ve C++ dillerine asina olan arkadasların da tahmin ettiği gibi
gösterici kavramı ile referans veri tipleri arasında çok fazla fark yoktur. Fakat C# dilinde
kullanıcının direkt olarak kullanabileceği bir gösterici veri türü tanımlamak yoktur. Bunun
yerine bazı değiskenler değer tip bazıları ise referans tipi olarak islem görürler. Peki
bunlar nelerdir? Temel veri tipleri olan int,double, float ve yapı nesneleri gibi veri türleri
değer tipler, herhangi bir sınıf türü ise referans türüdür. Đki değer tipi nesnesini birbirine
esitlerken değiskenlerde saklanan değerler kopyalanarak esitlenir ve bu durumda iki yeni
bağımsız nesne elde edilmis olur yani birinin değerini değistirmek diğerini etkilemez,
ancak iki referans tipini birbirlerine esitlediğimizde bu nesnelerde tutulan veriler
kopyalanmaz, islem yapılan nesnelerin heap bölgesindeki adresleridir, yani iki nesne de
aslında heap bellek bölgesinde aynı adresi gösterecekleri için birinde yapılan değisiklik
diğerini de etkileyecektir. Referans tiplerini tanımlarken herhangi bir adresi
göstermediğini belirtmek için null değere atanırlar.(Mesela: y = null ;)
CTS (Common Type System) Tipleri
.NET bir yazılım gelistirme platformudur. Aslında bütün veri tipleri CTS dediğimiz bir
sistem ile tanınırlar. Yani C# dilinde ki veri türleri aslında CTS 'deki veri türleri için birer
arayüz gibidirler. CTS sayesinde .NET platformu için gelistirilen bütün diller aynı veri
tiplerini kullanırlar, tek değisen veri türleni tanımlama yöntemi ve sentaksıdır. Bu yüzden
bizim C# dili ile tanımlayacağımız her veri tipinin CTS 'de bir karsılığı mevcuttur. Bu veri
türleri ve CTS karsılıkları asağıda tablolar halinde mevcuttur.
C# dilinde tanımladığımız bütün basit veri tipleri aslında CTS 'de bulunan bir yapı
nesnesidir.C# dilindeki önceden tanımlanmıs temel veri tipleri on bes tanedir. Bunlardan
on üçü değer tipi ikisi de değer tipidir.
Önceden Tanımlanmıs Value Veri Tipleri
Asağıda temel value tiplerin C# dilindeki adı, CTS karsılığı, açıklaması ve kullanım aralığı
bulunmaktadır.
C#
taki
adı
CTS Karsılığı Açıklama Max ve Min aralık yada değeri
sbyte System.Byte
8 bit isaretli
tamsayı
-128 : 127
short System.Int16
16 bit isaretli
tamsayı
-32.768 : 32.767
int System.Int32
32 bit isaretli
tamsayı
-2.147.483.648 : 2.147.483.647
long System.Int64
64 bit isaretli
tamsayı
-9.223.372.036.854.775.808 : -
9.223.372.036.854.775.807
byte System.Byte
8 bit isaretsiz
tamsayı
0 : 255
ushort System.UInt16
16 bit isaretsiz
tamsayı
0 : 65.535
uint System.UInt32
32 bit isaretsiz
tamsayı
0 : 4.294.967.295
ulong System.UInt64
64 bit isaretsiz
tamsayı
0 : 18.446.744.073.709.551.615
float System.Single
32 bit tek kayan
sayı
+yada - 1,5*10-45 : + ya da - 3,4*1038
double Sytem.Double
64 bit çift kayan
sayı
+yada - 5*10-324 : + ya da - 1,7*10308
decimal System.Decimal
128 bit ondalıklı
sayı
+yada - 1,5*10-28 : + ya da - 7,9*1028
bool System.Boolean true ya da false
char System.Char
Karakterleri
temsil eder
16 Unicode karakterleri
Simdi tabloda verilen veri türleri ile ilgili tanımlamalara örnekler verelim :
long a = 0xEF20 ; // 0x öneki sayıları hexadecimal olarak yazmamızı sağlar.
ulong ul = 5698UL ; // Sayının sonuna UL koyarak UnsignedLong olduğunu belirtiyoruz.
float fl = 3.14f ;
decimal d = 65.25M;
bool b = false ;
char ch1 = 'a' , ch2 = '\\' , ' \" ' , 'm' ;
Önceden Tanımlanmıs Reference Veri Tipleri
C# dilinde önceden tanımlanmıs iki tane referans tipi vardır. Bunlar string ve object
türleridir. Object türü C# dilinde bütün türlerin türediği bir sınıf yapısıdır. Kullanıcı
tarafından sonradan tanımlanacak bütün veri tipleri de aslında Object türünden türemis
olacaktır. Bu da object türünden bir nesneye herhangi bir veri türünden nesneyi
atayabileceğimiz anlamına gelir. Çünkü C# dilinde bütün nesneler bir object'dir. object
'ler özellistirilerek farklı amaçlar için kullanılır. Herhangi bir nesneyi object türü ile
eslestirme kavramı boxing olarak adlandırılır. Boxing ve bunun tersi islemi olan unboxing
kavramlarını daha sonraki makalelerimizde detaylı olarak inceleyeceğiz.
Diğer bir referans tipi ise string türüdür. C ve C++ gibi dillerde string islemleri
yapabilmek için karakter dizileri tanımlanır ve bunlar string olarak isleme alınırlar ancak
C# dilinde karakter dizileri tanımlamak yerine string adı ile yeni bir türü mevcuttur.
String veri türü birtakım yararlı isler daha kolay bir sekilde yapılmaktadır. Mesela asağıda
iki string' in + operatörüyle arka arkaya nasıl eklendiği gösterilmektedir. + operatörü
burada string sınıfı için yüklenmistir(overload). Overloading kavramı baslı basına bir
makale konusu olduğu için burada değinmeyeceğim.
string s1 = "Hello " ;
string s2 = ".NET" ;
string s3 = s1 + s2;
Bir dilin sentaksı açısından özel anlamlar ifade eden karakterleri kullanmak istiyorsak
bunları \ (escape) ifadesiyle belirtmek gerekir. Mesela bir dizin bilgisini içeren bir string
nesnesini asağıdaki gibi tanımlarız.
string yol = "C:\\docs\\xxx\\" ;// Bu tür kullanıma escape sequence kullanımı denir.
Escape sequence 'leri kullanmak yerine string içinde görünen ifadenin aynısı belirtmek
için string ifadesinin önüne @ isareti kullanılır.Mesela ;
string esc = @"C:\docs\xxx\" // böyle bir kullanımda escape karakterini kullanmayı
kaldırmıs oluyoruz.
C# taki adı CTS Karsılığı Açıklama
object System.Object
Bütün veri türlerinin türediği kök
eleman
string System.String Unicode karakterlerinden olusan string
Yukarıda C# dilindeki temel referans veri türleri tablo halinde gösterilmistir.
Bir sonraki makalemizde C# temel kontrol yapılarını göreceğiz.
C# ile Windows Registry Đslemleri(Microsoft.Win32)
Hemen hemen her profesyonel uygulamada gördüğümüz Registry'ye yazma ve ordan
okuma islemlerinin nasıl yapıldığını basit bir uygulama ile anlatacağız. Düsününki bir
uygulama gelistirdik ve uygulama her çalıstığında kullanıcıyı selamlamak istiyoruz ve
uygulamayı kaçıncı defa çalıstırdığını söylemek istiyoruz ona. Bunun bir çok yolu olmasına
rağmen en güzel ve en güvenilir yolu ilgili bilgileri Windows un registry dediğimiz
bölgesinde tutmaktır. registry dediğimiz yerler olmasaydı pek ala bu isi dosyaya yazma
ve okumayla da yapabilirdik. Regsitery bölgesini okuma ve yazma amaçlı .NET framework
sınıf kütüphanalerinden faydalanacağız. Bu sınıflar Microsft.Win32 isimalanının altında
bulunmaktadır. Bu sınıfların en çok kullanılan metodlarını ve özelliklerini anlatmaya
baslamadan önce programızın yapısını kısaca anlatayım.
Bir console uygulaması olusturacağız. Program ilk çalıstığında bize bundan sonraki
açılıslarında bizi selamlaması için adımızı soracak.Daha sonra programı çalıstırdığımızda
"Hosgeldin Sefer. Programı 3. defa çalıstırıyorsunuz." diyecek. Programın kaç defa
çalıstığını anlamak için ise program ilk açıldığında registry bölgesine "1" değerini
yazacağız ve programın her çalıstığında o değeri bir artıracağız. Böylece programın kaç
defa çalıstığını öğrenmis olacağız. Tabi eğer Windows un <regedit> aracıyla daha
önceden uğrastıysanız bizim programlama yoluyla değistirdiğimiz değerleri kendi
ellerinizle gidip değistirebilirsiniz. Demek istediğim burda sifre ve kullanıcı adı gibi bazı
kisiye özel bilgilerin saklanması pek güvenli değildir.
Eğer su ana kadar registry hakkında bir bilginiz yoksa Start->Run ' menusune gelip
regedit yazarak registry hakkında biraz bilgi edinebilirsiniz. Bu programla rastgele
değerler silerseniz bazı programlarınız zarar görebileceği için tavsiyem her hangi bir silme
islemi yapmayan ve sadece neler olup
bittiğine bakın.
Simdi C# ın büyük bir kolaylık sağladığı registry yazma ve okuma için gelistirilmis
RegistryKey sınıfının islevlerini görelim.
:::: RegistryKey Sınıfı(Microsoft.Win32) ::::.
Bildiğiniz gibi windowsun register yapısı ağaç seklindeki klasörlere benzer. Her yeni
anahtar altında bir alt anahtar açabildiğimiz gibi anahtarlar altında yeni "string" yada "int"
gibi değerler olusturup programla ilgili istediğimiz değerleri saklayabiliriz. Bu ise
klasörlerde olusturduğumuz dosyalara benzer. Daha öncede dediğimiz gibi buraya regedit
le kolayca ulasabildiğimiz için güvenlik amaçlı bilgileri (sifre vs) veya programımızla ilgili
kritik bilgileri(serial number vs) burada saklamamamız gerekir. Biz bu programdaki
bilgilerimizi HKEY_LOCAL_MACHINE\Software altında csnedir isimli bir alt anahtar
olusturarak kaydedeceğiz.
RegisteryKey sınıfı türünden bir nesne olusturmak için ya RegiteryKey sınıfının static üye
fonksiyonu olan OpenSubKey() metodunu yada yada Register sınıfının static üyelerini
kullanırız. Asğıda detaylı olarak bu metodlar hakkında bilgi bulabilirsiniz.
:: CreateSubKey() Metodu ::
Geriye RegistryKey türünden bir nesne dödüren bu fonksiyon yeni bir alt anahtar
olusturur yada var olan bir anahtarı okumak için açar.Fonksiyonun prototipi asağıdaki
gibidir. Unutmayın bu metodu kullanabilmek için ilgili kullanıcının register bölgesine
erisim hakkının olması gerekir. Aksi halde SecurityException hatası olusur.
public RegistryKey CreateSubKey(string subkey);
:: OpenSubKey() Metodu ::
Bu metod iki sekilde kullanılabilir, overload edilmis iki metod asağıdaki gibidir.
public RegistryKey OpenSubKey(string);//Bu metod anahtar okumak amcıyla kullanılır ve
geriye RegisteryKey döndürür.
public RegistryKey OpenSubKey(string,bool);//Bu metod ilk metod ile aynıdır fakat eğer
açılacak anahtara yazmada yapacaksak ikinci parametreyi true olarak girmemiz gerekir.
Varsayalın olarak ReadOnly açılır.
:: DeleteSubKey() Metodu ::
Bu metod iki sekilde kullanılabilir, overload edilmis iki metod asağıdaki gibidir.
public void DeletSubKey(string);//Parametre olarak gönderilen alt anahtarı siler.
public void DeletSubKey(string,bool);//Parametre olarak gönderilen alt anahtarı
siler.Đkinci parametre ise belirtilen alt anahtarın olmaması durumunda
"ArgumentNullException" hatasının yakalnıp yakalanmayacağını gösterir.Eğer true ise bu
hata yakalanır, false ise herhangi birsey olmaz.
:: DeleteSubKeyTree() Metodu ::
Bu metod iki belirtilen anahtardaki bütün anahtarları siler.Bir dosyayı sildiğinide içindeki
tüm dosyaları sildiğiniz gibi.Prototipi asağıdaki gibidir.
public void DeletSubKeyTree(string);
:: DeleteValue() Metodu ::
Đki sekilde kullanılabilir.Parametre olarak belirtilen değeri anahtardan siler.Đkinci
parametre ise DeleteSubKey() metodunda olduğu gibi hata yakalanıp yakalanmayacağını
belirtir.
:: Flush() Metodu ::
Registry 'de yaptığımız değisiklikleri diske kaydetmek için bu metodun çağrılması gerekir.
:: GetSubKeyNames() Metodu ::
Be metod geriye döndürdüğü string dizisine ilgili anahtardaki alt anahtar isimlerini
doldurur.Prototipi asağıdaki gibidir.
public string[] GetSubKeyNames()
:: GetValue() Metodu ::
Đlgili anahtardaki değerin içeriğini object türü olarak geri dönderir.Đki sekilde kullanılabilir.
Parametrik yapısı asağıdaki gibidir.
public object GetValue(string)
public object GetValue(string,object) //eğer değer yoksa varsayılan olarak parametre
olarak verilen object geriye döner.
:: GetValueNames() Metodu ::
Đlgili anahtardaki bütün değerleri bir string dizine aktarır.Parametrik yapısı asağıdaki
gibidir
public string[] GetValueNames()
:: SetValue() Metodu ::
Birinci parametresi ile belirtilen anahtara ikinci parametresi ile belirtilen bilgi
aktarılır.Parametrik yapısı asağıdaki gibidir.
public void SetValue(string,object)
:: Name Özelliği ::
Taban anahtardan itibaren(mesela HKEY_LOCAL_MACHINE) ilgili anahtarın tam yolunu
verir.
:: ValueCount Özelliği ::
Anahtarda bulunan değerlerin sayısını verir.
RegistryKey sınıfının üye elmanları ve olusturduğu exception sınıfları ile ilgili detaylı bilgiyi
MSDN Online' dan yada .NET Framework SDK Documentation ' dan edinebilirsiniz.
Simdi yazımızın basında bahsettiğimiz örnek uygulamamıza göz atalım.Asağıda bulunan
kaynak kodda satır aralarına size yardımcı olacak yorumlar ekledim.
//registry.cs
using System;
using System.Win32
//RegistryKey sınıfını kaynakkodda direkt kullanabilmek için bu isimalanını ekledik.
class CsReg
{
public static void Main()
{
RegistryKey register;
register = Registry.LocalMachine.OpenSubKey(@"Software\csnedir",true);
//HKEY_LOCAL_MACHINE/Software/csnedir anahtarını olusturup anahtara yazma
modunda açıyoruz.
if (register.GetValue("ad") == null)
{
/*Bu if bloğunda programın ilk defa çalısması durumu ile ilgili islemler
yapılıyor.Programın ilk defa çalıstığını register.GetValue("ad") ==null ifadesi ile anlıyoruz.
Kullanıcıdan isim alınıp registry de "ad" isimli anahtara yazılıyor ve tabili "Oturum"
adınıda programı bir defa çalıstırdığını belirten 1 değeri yazılıyor*/
Console.WriteLine("Lütfen adinizi yaziniz");
string ad = Console.ReadLine();
register.SetValue("ad",(string)ad);
register.SetValue("oturum",1);
Console.WriteLine("Tesekkürler...");
}
else
{
/*Bu blokta ise programın sonraki çalısmaları ile ilgili islemler yapılıyor. Oturum
sayısı registry den okunup aritmetik isem yapabilmek için ilgili formata dönüstürüdükten
sonra tekrar yeni değeri ile registry ye yazılıyor.Aynı sekilde registry den "ad" değeri
alınarak kullanıcı selamlanıyor.*/
string ad = (string)register.GetValue("ad");
int oturum_sayisi=Convert.ToInt32(register.GetValue("oturum"))+ 1;
register.SetValue("oturum",oturum_sayisi);
Console.WriteLine("Hosgeldin " + ad);
Console.WriteLine("Programi " + oturum_sayisi + " kez açtiniz");
Registry.LocalMachine.Flush();
}
}
}
Assembly, ILDASM.exe ve GACUTIL.exe Hakkında
Bu makalede kavram olarak en çok karıstırılan ve anlasılması diğer konulara göre zor
olan Assembly kavramını ve Visual Studio ile birlikte gelen GACUTIL ve ILDASM gibi
önemli araçları inceleyecegiz.
Asembly Nedir?
Hemen ilk basta belirtelim ki bu makalede bahsedeceğimiz Assembly'nin alt seviye bir
programlama dili olan Assembly ile yakından uzaktan hiçbir alakası yoktur. Sadece bir
isim benzerliği vardır. .NET platformunda yazdığımız kodlar sonucunda olusturduğumuz
bütün .exe uzantılı dosyalara ve .dll uzantılı dosyalara genel olarak Assembly
denilmektedir. Projemize ait derlenmis kodlar ve metadata dediğimiz bir takım
özniteleyici kodlar Assembly'ler içerisinde bulunur. Assembly'lerin kabaca özellikleri
asağıdaki gibi sıralanabilir.
1-) Assembly'lerde metadata denilen veriler, Assembly'deki tür bilgileri ve baska
kaynaklarla olan bağlantılar saklanır.
2-) Assembly'de(dll yada exe) kendilerine ait versiyon bilgisi tutulur. Hatırlarsanız klasik
dll ve exe tipi dosyalarda versiyon bilgisi saklanmadığı için çesitli uyumsuzluklar
yasanabilmekteydi. Mesela farklı iki firmanın hazırladığı dll 'ler aynı isimli olduğunda
sonradan register edilen dll halihazırda bulunan dll 'in üzerinde yazıldığı için sistemde
bulunan bazı uygulamalarda sorun çıkıyordu. Dll 'ler bu tür sorunlara yol açtığı için DLL
Hell (Dll cehennemi) kavramı ortaya çıkmıstı. Aslında COM dll 'leri ile bu sorun bir nebze
ortadan kalkmıssa da asıl çözüm Assembly'ler ile gelmistir.
3-) Assembly'lerde versiyon bilgisi saklandığı için bir uygulama içerisinde farklı
versiyonlara sahip Assembly'leri kullanabiliriz.
4-) Program kurma islemi, Assembly 'ye iliskin dosyayı direkt kopyalayarak yapılabilir.
Eski sistemde DLL 'lerin register edilmesi gerekiyordu.
Assembly'lerin en önemli özelliklerinden birisi de Application Domain dediğimiz
kavramdır. Application Domain sayesinde bir proses içinde birden fazla birbirinden
bağımsız çalısan Assembly 'yi çalıstırma imkanına kavusuruz. Bu konuya açıklayıcı bir
örnek olması açısından asağıdaki örneği inceleyebilirsiniz.
Bu örnekte iki tane Console uygulaması yapacağız. Bu iki uygulamaya ait çalısır
durumdaki dosyalara artık Assembly diyebilirsiniz. Asağıdaki ilk örnekte Main islevi
içerisinde ekrana bir yazı yazdırıyoruz.
//assembly1.cs
using System;
namespace assembly1
{
class csnedir1
{
static void Main(string[] args)
{
Console.WriteLine("Beni disardan yüklediler.");
}
}
}
Bu programı notepad 'da yazıp derledikten sonra komut satırında "csc assembly1.cs"
yazıp assmbly1.exe dosyasını olusturun. Aynı klasör içine simdi asağıdaki assembly2.cs
dosyasını olusturun ve derleyerek assembly2.exe 'nin olusmasını sağlayın.
//assembly2.cs
using System;
namespace assembly2
{
class csnedir2
{
static void Main(string[] args)
{
AppDomain apd2 = AppDomain.CreateDomain("Csnedir");
apd2.ExecuteAssembly("assembly1.exe");
}
}
}
Yukarıda da bahsettiğim gibi Application Domain sayesinde bir uygulama içerisine değisik
assembly'ler yüklenebilir ve çalıstırılabilir. Application Domian kavramını System isim
alanında bulunan AppDomain sınıfı temsil eder. Yeni bir Application Domain'i olusturmak
için AppDomain sınıfının overload (asırı yüklenmis)edilmis CreateDomain statik üye
fonksiyonları kullanılır. AppDomain sınıfının ExecuteAssembly fonksiyonuyla dısarıdan
yeni bir Assembly yüklenir ve o satırdan itibaren yüklenen assembly çalıstırılır.
(Yukarıdaki örnekte iki Assembly'nin de aynı klasörde olmasına dikkat edin.)
Assembly hakkında bu genis giris bilgisini verdikten sonra Assembly'nin fiziksel
yapısından bahsedelim biraz. Bir Assembly belgesinde Assembly metadata, tür(type)
metadata, kaynaklar ve IL(Intermadiate Language) kodu bulunur. Bütün bu yapılar bir
Assembly dosyasında bulunabileceği gibi Assembly metadata'lar sayesinde dısarıdaki
kaynaklara referans da verilebilir. Assembly'lerin en önemli yapısı Manifest dediğimiz
parçasıdır. Manifest assembly metadata'lar bulunur. Peki nedir bu Assembly
metadata'lar? Bir Asembly adı, versiyonu gibi kimlik bilgileri, ilgili Assembly ile ilgili olan
diğer dosyalar, baska Assembly'lere olan referanslar gibi bilgilerin tamamına Assembly
metadata denir. Đste bu bilgilerin olusturduğu kümeye Manifest denilmektedir.
Assembly'lerin Manifest bölümünde bulunan elemanlar temel olarak asağıdaki tabloda
verilmistir.
Özellik Anlamı
AssemblyCompany Assembly'nin firma bilgisi
AssemblyCopyright Copyright bilgisi
AssemblyCulture Đlgili Assembly'ye ait kültür
bilgisi(Almanya,Đngiltere,Türkiye
vs..)
AssemblyDelaySign Gecikmeli imzanın olup
olmayacağı (True ya da False)
AssemblyDescription Assembly ile ilgili kısa açıklama
AssemblyFileVersion Win32 sistemindeki dosya
versiyonu
AssemblyInformationalVersion CLR tarafından kullanılmayan ve
okunabilirliği yüksek olan
versiyon bilgisi
AssemblyKeyFile Assembly'nin kayıt edilmesi için
gereken anahtarın bulunduğu
dosya
AssemblyKeyName Kayıt için gereken anahtar
sözcük
AssemblyProduct Ürün adı
AssemblyTitle Assembly'nin adı
AssemblyTrademark Trademark bilgisi
AssemblyVersion String seklindeki Version
numarası
Bu tablo MSDN kitaplığından alınmıstır
Assembly'ler private ve shared olmak üzere ikiye ayrılır. Bunları detaylı olarak açıklamaya
baslamadan önce Assembly'leri görüntülemek için kullanılan ILDASM.exe aracını
inceleyelim. ILDASM.exe MSIL kodu içeren assembly dosyalarını okur ve kullanıslı bir
arayüz ile kullanıcıya sunar. ILDASM.exe programını çalıstırmak için komut satırına
ILDASM.exe yazmamız yeterlidir. Açılacak pencereden File->Open menüsünü kullanarak
görüntülemek istediğiniz Assembly dosyasını seçin.(Asağıda gördüğünüz ekran
görüntüleri Assembly2.exe'nin görüntüleridir.) Bir assembly'deki türler, sınıflar,
fonksiyonlar çesitli sembollerle temsil edilmistir. Hangi sembollerin ne anlama geldiğini
MSDN kitaplığından bulabilirsiniz.
Simdi de açılacak pencereden Main bölümüne tıklayın, ve asağıdaki görüntüyü elde edin.
Buradaki bütün kodlar IL kodudur.
Diğer bölümlerde tıklayarak IL kodlarını inceleyebilirsiniz.
Yukarıda bahsettiğimiz gibi Assembly'ler private ve shared olmak üzere ikiye ayrılır.
Normal olarak gelistirilen Assembly'ler private Assembly olarak adlandırılır. Bu tür
assembly'ler uygulama ile aynı dizinde ya da alt dizinlerde bulunur. Versiyon ve isim
uyusmazlığı sorunu bu tür assembly'lerde olmamaktadır. Shared assembly'ler ise daha
çok büyük projelerde mesela bir projenin bir firmaya ait farklı ofislerinde gerçeklestirildiği
durumlarda kullanılır. Bu durumda assembly'lerin uyması gereken bazı kurallar vardır.
Bunlardan en önemlisi strong name dediğimiz tekil bir isme sahip olmasıdır. Bu sayede
shared assembly'ler tekil olduğu için bir sistemde global düzeyde islem görürler. Yani
farklı farklı uygulamalardan aynı anda shared assembly'lere ulasılabilir. Simdi shared
assembly kavramını biraz açalım.
Shared Assembly
Assembly'ler varsayılan olarak private'tır. Bu yüzden bu tür Assembly'ler sadece
bulundukları dizin içerisindeki uygulamalar tarafından görülür ve çalıstırılırlar. Oysa ki
bazı durumlarda bütün programlar tarafından o Assembly'ye birbirlerinden bağımsız
olarak erisilmesini isteriz. Bunu sağlamak için Global Assembly Cache denilen bir
sistemden faydalanacağız. .NET 'in yüklü olduğu bütün sistemlerde Assembly Cache
mekanizması vardır. Bu Assembly Cache'ye yüklü olan Assembly'ler bütün uygulamalar
tarafından kullanılabilir. Bir assembly'yi GAC'a(Global Assembly Cache) yüklemek için 3
yöntem kullanılır:
- Assembly'leri Windows Installer 2.0 ile yüklemek
- Gacutil.exe isimli .NET ile gelen aracı kullanmak
- /assembly 'ye Assembly dosyasını kopyalamak (C:\winnt\assembly\)
Simdi adım adım bir shared assembly olusturmayı görelim. Bunun için ikinci yöntemi
kullanacağım. Baslangıç olarak gacutil.exe programı hakkında bilgi vermek istiyorum.
Gacutil(global assembly cache utility) programı .NET ile birlikte gelir. Assembly cache'ye
yeni assembly yüklemek varolan assembly'leri listelemek ve silmek için kullanılır. Komut
satırından asağıdaki parametrelerle bu programı çalıstırabiliriz.
* gacutil /l ---> GAC 'da bulunan bütün assembly'leri listeler.
* gacutil /i assemblydll---> assemblydll adlı shared assembly'yi GAC 'a yükler.
* gacutil /u assemblydll---> assemblydll adlı shared assembly GAC 'dan siler.
Đlk adım olarak bütün shared assembly'lere strong name dediğimiz bir isim vermeliyiz.
GAC 'da bulunan bütün assembly'lerin farklı bir adı vardır. COM teknolojisindeki globally
unique identifier(GUID) 'e benzetebiliriz bu isimleri. Peki bu strong name 'leri nasıl
olusturacağız? Bu is için yine .NET ile birlikte gelen sn.exe adlı aracı kullanacağız. sn
programı asağıdaki gibi kullanılarak bir tekil isim olusturulur ve anahtar.snk adlı
dosyaya yazılır.
sn -k anahtar.snk
anahtar.snk dosyasını olusturduktan sonra bu anahtar ismi projemizdeki AssemblyInfo.cs
dosyasındaki manifeset bölümüne ekliyoruz. Yani AssemblyKeyFile özelliğini anahtar.snk
olarak değistiriyoruz. Sonuç olarak AssemblyInfo.cs dosyası asağıdaki gibi olacaktır.
...............
[assembly AssemblyDelaySign(false)]
[assembly AssemblyKeyFile("../../anahtar.snk")]
[assembly AssemblyKeyName("")]
...............
Bu islemleri yaptıktan sonra projeyi "Build" edip olusan Assembly dosyasını gacutil.exe
yardımıyla GAC 'a ekliyoruz. Bu islemi komut satırından asağıdaki gibi yapmalıyız.
gacutil /i Assembly1.dll
Bu islemi yaptıktan sonra shared olan bu assembly'ye istediğimiz .NET projesinden
ulasabiliriz. Tabiproject->Add reference menüsünden shared assembly'ye referans
verdikten sonra yapabiliriz bunu. Shared assembly olduğu için yeni bir projede bu
assembly kullandığımız da lokal bir kopyası olusturulmaz dolayısıyla GAC üzerinden
erisilir.
Kaynaklar
C# Primer (A Practical Approach) - Stanley Lipman
MSDN Kütüphanesi
Kaynak Dosyalarının Kullanımı(Resource Files)
Bir uygulamanın içindeki kaynaklar neler olabilir; resimler, müzikler ve yazılar(string). Bu
makalede bu tür kaynakların, harici olarak programımıza nasıl ekleneceğini öğreneceğiz.
Derlenmis bir program içerisinde bir yazıyı değistirmek çok zordur. Bu yüzden sonradan
değisme ihtimali bulunan kaynakları yönetmek için .NET platformu bizim için büyük
kolaylıklar sağlamıstır. Bazı durumlarda da programımızın farklı dillerdeki versiyonları
olabilir. Bu durumda her dil için bir string kaynak dosyası(resource file) hazırlamamız
yetecektir.
Bu makalede, .NET ile birlikte gelen ve kaynak dosyaları olusturmada kullanılan
resgen.exe adlı programı, System.Resources isimalanında bulunan ResourceWriter
isimli sınıfı, ve bu olusturulan kaynak dosyaları kullanmak için yine System.Resources
isimalanında bulunan ResourceManager adlı sınıflarının kullanımını göreceğiz. Ve tabiki
bunları anlatırken basit bir uygulama üzerinden anlatacağım.
Yukarıda da bahsettiğim gibi kaynak dosyalarda resim ve yazılar bulunabilir.Đlk adımda
basit bir .txt dosyasına istediğimiz yazıları alt alta yazalım. Resgen.exe yardımıyla bu txt
dosyasından .NET platformu için özel bir kaynak dosyası olusturacağız. Yalnız dikkat
etmemiz gereken nokta su : bu sekilde hazırlanacak bir kaynak dosyasına resimleri
ekleyemiyoruz. Eğer kaynaklarımız sadece yazılar ise bu yöntemi kullanıyoruz. Eğer
kaynak olarak resim eklemek istiyorsak birazdan anlatacağım ResourceWriter sınıfını
kullanarak basit bir program yazacağız. Simdi asağıdaki terimler.txt dosyasını olusturun.
Pointer = Gösterici
Function = Fonksiyon
Array = Dizi
Template = Sablon
yazilar.txt
Simdi resgen.exe yardımıyla yazilar.txt den yazilar.resources adlı kaynak dosyayı
olusturalım. resgen.exe yi çalıstırmak için Start-> Programs -> Microsft Visual
Studio.NET -> Visual Studio.NET Tools -> Visual Studio.NET Command Prompt yolunu
kullanabilirsiniz.
Konsol ekranına
resgen yazilar.txt
yazarak yazilar.resources dosyasının olusmasını sağlayan. yazilar.resources dosyasını bu
sekilde kullanabileceğimiz gibi XML formatında bir kaynak dosyası da olusturabiliriz.
Bunun içinde konsol ekranına asağıdaki komutu yazın.
resgen yazilar.resources yazilar.resx
Kaynak dosyalarının nasıl kullanıldığına geçmeden önce resimlerin de eklenebileceği bir
kaynak dosyası hazırlayan bir program yazalım. Bu programda yukarıda da dediğim gibi
System.Resources isimalanı altında bulunan ResourceWriter sınıfını kullanacağız. Bunun
için asağıdaki programı yazıyorum.
using System;
using System.Drawing;
using System.Resources;
class Class1
{
static void Main(string[] args)
{
ResourceWriter resw = new ResourceWriter("yazilar2.resources");
Image resim = Image.FromFile("logo.gif");
string anahtar,deger;
for(int i=0 ; i<=3 ; i++)
{
Console.Write("Kaynak için anahtar kelime girin: ");
anahtar = Console.ReadLine();
Console.WriteLine();
Console.WriteLine("Girdiginiz anahtarin degerini girini: ");
deger = Console.ReadLine();
resw.AddResource(anahtar,deger);
}
resw.AddResource("Cslogo",resim);
resw.Close();
}
}
Biraz programı açıklamakta fayda var. Visual Studio.NET 'de yeni bir Console Uygulaması
açın. Resimlerle is yapabilmek için daha doğrusu Image sınıfını kullanabilmek için
System.Drawing isimalanını eklememiz gerekir. Bunun için Project->Add Reference
menüsünü kullanıp System.Drawing.dll i için projemize referans verelim. Kaynak dosyayı
olusturmak için ise System.Resources isimalanında bulunan ResourceWriter sınıfını
kullanıyoruz. Programımızın basında yeni bir ResourceWriter nesnesi olusturuyoruz.
Varsayılan yapıcı islevine ise olusturacağımız kaynak dosyasının ismini veriyoruz. Daha
sonra kaynak dosyasına ekleyeceğimiz bir resim dosyasından Image türünden bir nesne
tanımlıyoruz. Yine aynı sekilde Image sınıfının yapıcı islevine resim dosyasının adını
gönderiyoruz. Dosyanın çalısan programla aynı klasör içinde olmasına dikkat edin. Aksi
halde FileNotFoundException hatası alırız. Daha sonra bir for döngüsü yardımıyla 4 defa
kullanıcıdan kaynak için anahtar ve değer girilmesini istiyoruz. Kaynak dosyasına
kaynakları eklemek için ResourceWriter sınıfının overload edilmis iki üye islevini
kullanıyoruz. Bu iki üye islevinin prototipi asağıdaki gibidir.
public void AddResource(string, object); // bu fonksiyonu kaynağa Image nesnesini
eklemek için kullanıyoruz.
public void AddResource(string, string); // bu fonksiyonu ise iki string anahtar-değer
ikilisini kaynak dosyasına girmek için kullanıyoruz.
Son olarak Close islevi ile hafızada bulunan bilgiler yazilar.resource dosyasına yazılır. Bu
islevi kullanmadığımızda bilgiler dosyaya kaydedilmeyecektir.
Bu sekilde olusturduğumuz kaynak dosyasının resgen.exe ile olusturduğumuzdan tek
farklı kaynak dosyasına bir resim bilgisinin binary olarak yerlestirilmesidir. Kaynak
dosyası olusturmanın iki yöntemini gördükten sonra simdi bu kaynak dosyaları bir
uygulamada nasıl kullanacağımızı görelim. Bu amaçla yeni bir Windows Uygulaması
baslatalım. Amacımız bir picturebox yardımıyla kaynak dosyadaki binary resim bilgilerini
göstermek ve yazıları da bir label kontrolu üzerinde göstermek. Öncelikle Visual
Studio.NET 'de bulunan solution explorer penceresinden projemize hazırladığımız kaynak
dosyasını eklememiz gerekir. Bunun için solution explorer'daki projemize sağ tıklayıp
Add->Add Existing Item 'dan yazilar2.resource adlı kaynak dosyasını seçelim. Bu
yöntemle kaynak dosyası projemize Embed Resource olarak eklenecektir. Properties
penceresindeki Build Action özelliğini kullanarak bu ayarı değistirebiliriz. Kaynak dosyasını
ekledikten sonra asağıdaki gibi bir form penceresi tasarlayın. Form üzerine bir picturebox
ve 4 tane label kontrolu ekleyin.
Formumuzu tasarladıktan sonra Form1 'in load metodunu asağıdaki gibi düzenleyin.
private void Form1_Load(object sender, System.EventArgs e)
{
ResourceManager rsm = new
ResourceManager("winAppRes.yazilar2",Assembly.GetExecutingAssembly());
pictureBox1.Image = (Image)rsm.GetObject("Cslogo");
label1.Text = rsm.GetString("Pointer");
label2.Text = rsm.GetString("Function");
label3.Text = rsm.GetString("array");
label4.Text = rsm.GetString("Template");
}
Yukarıdaki metotda form1 yüklendiğinde ResourceManager yardımıyla kaynak dosyadaki
bilgileri ,form üzerindeki kontrollere yerlestiriyoruz. Bu kodun çalısabilmesi için
System.Reflection(Assembly sınıfı için) ve System.Resources(ResourceManager sınıfı için)
isimalanlarının projeye using ile eklenmesi gerekir. Đlk olarak o an üzerinde çalısılan
assembly için bir ResourceManager nesnesi olusturuyoruz. Yeni bir ResourceManager
nesnesi olustururken ResourceManager 'in yapıcı islevine kaynak dosyanın projedeki
göreceli yolunu ve o an üzerinde çalıstığımız Assembly nesnesini geçiyoruz. Benim
olusturduğum projenin ismi winAppRes olduğu için 1. parametre "winAppres.yazilar2"
olmalıdır. (Dikkat edin 1. parametrede kaynak dosyasının uzantısı olan .resource ekini
yazmadık ). 2. parametreye ise Assembly(System.Reflection) sınıfının statik üye islevi
olan GetExecutingAssembly ile dönen Assembly nesnesini geçiyoruz. Kaynak
dosyasındaki verileri formun üzerindeki kontrollere yerlestirirken ResourceManager
sınıfının iki ayrı üye islevini kullanıyoruz. Bunlardan GetObject islevi ile picturebox 'a
kaynaktaki resmi aktarıyoruz. GetObject islevinin parametresi kaynak dosyasındaki
resime ait verinin anahtar adını geçiyoruz. Hatırlarsanız kaynak dosyayı olusturuken
resim için "Cslogo" anahtarını kullanmıstık. GetObject ile geriye dönen nesne object
türünden olduğu için resim bilgisini picturebox 'a yerlestirebilmek için
(Image)rsm.GetObject("Cslogo") ifadesiyle tür dönüsümü yapıyoruz. GetString islevi ile
de kaynak dosyasındaki yazıları alıyoruz. Bu islevden geriye dönen değer parametre
olarak verilen anahtara ait string değeri olduğu için label kontrollerinin text özelliğine
atayabiliriz.
Programı derledikten sonra asağıdaki ekran görüntüsünü elde etmelisiniz.
Not : Eğer kaynak dosyasını resgen.exe ile XML formatında hazırlasaydık yukarıdaki
islemlerin aynısı yine geçerli olacaktı. Benim tavsiyem kaynak dosyalarını XML formatında
hazırlamanız yönündedir. Hem Visual Studio.NET ortamında kaynak dosyayı daha rahat
düzenleyebilirsiniz hem de XML formatındaki dosyaya istediğiniz ortamlardan rahatlıkla
ulasabilirsiniz.
C#'da ArrayList Sınıfının Kullanımı
Programların çoğunda birden fazla aynı tipte değiskenlere ihtiyaç duyarız. Bu sorunun
çözümü olarak birçok dilde kullanılan veri yapıları ,dizilerdir. Bildiğimiz klasik dizilerin
programlama tekniklerine getirdikleri kolaylıkların dısında birtakım kısıtlamaları da vardır.
Bu makalede klasik dizilerde sık sık karsılastığımız çesitli sorunları ve bu sorunları nasıl
çözebileceğimizi inceleyeceğiz.
.NET platformunun sınıf kitaplıklarında bulunan ve programcıların islerini çok
kolaylastıran ArrayList sınıfı ile klasik dizilerde karsılastığımız sorunları nasıl çözeceğimizi
göreceğiz.
Klasik dizilerle çalısırken karsılasabileceğimiz temel sorunları su sekilde sıralamak
mümkündür:
• Dizilerin sınırları sabittir.
• Dizilerin tüm elemanları aynı türden olmalıdır.
• Kullanmadığımız dizi elemanlarından dolayı bellek alanları gereksiz yere
isgal edilmektedir.
Örneğin sayısını bilemediğimiz bir dizinin eleman sayısını 500 olarak belirlediğimizi
varsayalım. Çalısma zamanında dizimizin sadece 10 elamanını kullandığımız durumda
diğer 490 elemanlık bellek alanı bos olarak kalır. Öte yandan dizimizde tutmak istediğimiz
değiskenlerin sayısı 501 bir olduğu bir durumda "IndexOutOfRangeException" istisnai
durumu ortaya çıkar ve program bu hatadan dolayı sonlanır.
Mesela asağıdaki kodu derlemeye çalısalım:
using System;
class CshaprNedirCom
{
static void Main(string[] args)
{
int[] intDizi= new int[10];
try
{
intDizi[20]=5;
}
catch( Exception e)
{
Console.WriteLine(e.GetType());
}
Console.ReadLine();
}
}
Yukarıdaki programda intDizi 'mizi 10 eleman alacak sekilde tanımlamamıza rağmen bu
dizinin 20. elemanına ulasıp ona birseyler atamaya çalıstık. Bu durumda programımız
çalısırken hata verdi. Çünkü dizinin sınırları bellidir ve bu sınırların dısına çıkamıyoruz.
Eleman sayısı ihtiyacımıza göre değisen bir veri yapısı olması gerçekten hos olmaz mıydı?
Evet C#'da böyle bir dizi yapımız var. Bunun ismi ArrayList'tir.
ArrayList sınırları dinamik olarak değisebilen diziler olarak tanımlanır. Bu veri yapısı .NET
sınıf kütüphanesinin System.Collections isim alanında bulunur. Đsterseniz ArrayList'i
nasıl kullanacağımızı bir örnekle inceleyelim:
using System;
using System.Collections; // ArrayList sınıfnı kullanmak için
// System.Collection isimalanını eklemeliyiz..
class CshaprNedirCom
{
static void Main(string[] args)
{
ArrayList aList= new ArrayList(); // aList isimli ArrayList nesnesi
olusturalım.
// aList nesnemize sırası ile 5, 8, 1, 17 ve 20 değerlerini
// Add metodu ile ekleyelim.
aList.Add(5);
aList.Add(8);
aList.Add(1);
aList.Add(17);
aList.Add(20);
// aList'in elemanlarını ekrana yazdıryoruz:
Console.WriteLine("\t aList'in elemanları:");
foreach(int eleman in aList)
Console.WriteLine(eleman);
// aList dizimizden 8 ve 20 değerlerini çıkartalım:
aList.Remove(8);
aList.Remove(20);
// aList dizimize 66 ve 4 değerlerini ekleyelim:
aList.Add(66);
aList.Add(4);
Console.WriteLine("\n\t aList dizisinden 8 ve 20\' çıkartıp, 66 ve 4 ekledik:");
foreach(int eleman in aList)
Console.WriteLine(eleman);
Console.ReadLine();
}
}
Yukarıdaki örneğimizde öncelikle ArrayList sınıfını kullanmak için NET sınıf kütüphanesinin
System.Collections isim alanınını kullanacağımızı using System.Collections; ile
bildiriyoruz. Main fonksiyonumuzun içindeki ilk satırda, ArrayList aList= new ArrayList() ,
aList ismini verdiğimiz ArrayList sınıfından bir nesne olusturuyoruz. aList nesnemizi
olusturduğunuz satırdan sonraki bes satırda ArrayList sınıfının Add() metodu ile aList
adlı dizimize elemanları ekliyoruz.
Daha sonra aList diziminizin elemanlarını ForEach döngüsü ile tek tek ekrana
yazdıyoruz. ArrayList sınıfındaki bir nesnenin elemanlarını tek tek silmek için Remove()
metodunu kullanırız. Remove() metodu ile istediğimiz elemanı diziden atabiliriz. Biz de 8
ve 20 elemanlarını diziden attık. Son olarak dizimize 66 ve 20 elemanlarını ekleyip dizinin
son halini ekrana yazdırdık.
C# dilinde normal diziler bildiğiniz gibi sadece aynı tipten verileri tutar. Ama ArrayList
sınıfına ait dizilerimiz her türlü nesneyi aynı dizi içinde tutabilir. Yani aynı dizide int,
float, ve string tiplerindeki değiskenleri depolama sansımız var. Mesela asağıdaki kod
C# dili kuralları çerçevesinde geçerli bir koddur:
ArrayList karmaList= new ArrayList(); // karmaList isimli ArrayList nesnesi
olusturalım.
karmaList.Add("Ahmet");
karmaList.Add(12);
karmaList.Add(true);
karmaList.Add(32.4f);
karmaList.Add('c');
Bu kod ile karmaList isimli ArrayList nesnemizin içinde string, int, bool, float ve char
tiplerinden olusan verileri aynı anda saklarız. ArrayList sınıfının bize sunduğu diğer bir
güzel özellik ise tek bir komut ile ArrayList dizimizin içerisindeki elemanları ters
çevirebilmemizdir. Ters çevirme islemi için Reverse() metodu kullanılır. Đsterseniz
Reverse() metodunu ve ArrayList'lerde nasıl birden farklı türdeki elemanları
kullanacağımızı bir örnekle inceleyelim:
using System;
using System.Collections; // ArrayList sınıfnı kullanmak için
// System.Collection isimalanını eklemeliyiz..
class CshaprNedirCom
{
static void Main(string[] args)
{
// karmaList isimli ArrayList nesnesi olusturalım.
ArrayList karmaList= new ArrayList();
// karmaList'e değisik tipte elemanlar ekliyoruz.
karmaList.Add("Ali");
karmaList.Add(23);
karmaList.Add(false);
karmaList.Add(52.8d);
karmaList.Add('r');
// karmaList'in elemanlarını ekrana yazdırıyoruz:
Console.WriteLine("\t karmaList'in elemanları:");
foreach(object eleman in karmaList)
Console.WriteLine(eleman);
karmaList.Reverse(); // karmaList'imizi ters çeviriyoruz.
Console.WriteLine("\n ---> karmaList'in elemanları ve türleri <---");
foreach(object eleman in karmaList)
Console.WriteLine("Türü: {0,15} değeri: {1,7}",eleman.GetType(), eleman);
Console.ReadLine();
}
}
Yukarıdaki programda karmaList isimli ArrayList sınfından bir nesne olusturduktan sonra
onu değisik türlerden veriler ile doldurduk. Bu dizinin elemanlarını sıra ile foreach
döngüsü yardımıyla ekrana yazdırdık. karmaList.Reverse() satırında ise dizimizi ters
çevirdik. Son isimizde ise karmaList dizisinin elemanlarını ekrana tek tek yazdırırken aynı
zamanda eleman.GetType( ) her elemanın türünü bulup yazdık.
Makalemizi ArrayList sınıfının temel metodlarının tablosunu vererek bitirmek isterim. Bu
tablodaki metodların isimize yaracağı kanaatindeyim.
Add Bir nesneyi ArrayList'in sonuna ekler.
BinarySearch
Sıralanmıs bir ArrayList içinde bir nesneyi Binary
search algoritması kullanarak arar.
Clear ArrayList'in tüm elemanlarını siler. Sıfırlar.
Contains
Herhangi bir nesnenin ArrayList'in elemanı olup
olmadığını kontrol eder.
Insert
Dizinin sonuna değilde istediğimiz bir yerine
indeksini belirterek eklememizi sağlar.
Remove Herhangi bir elemanı diziden siler.
Reverse Diziyi ters çevirir.
Sort ArrayList'i sıralar.
Her Yönüyle C#'da Yığın (Stack) Sınıfı
Bu yazımızda önemli veri yapılarından olan yığın (Stack) veri yapılarına giris yapacağız.
Yığın veri yapılarının çalısma mantıklarını anladıktan sonra .NET sınıf kitaplıklarında
yeralan Stack sınıfını C#'da nasıl kullanacağımızı inceleyeceğiz.
1. Yığın veri Yapısının Çalısma Sekli
Yığınlar genelde aynı tipten verilerin tutulduğu ve Son Giren Đlk Çıkar (LIFO) çalısma
mantğını kullanan veri yapıları olarak tanımlanır. Bir yığına en son giren eleman ilk olarak
çıkar. Yığınları anlatırken en çok üst üste konmus tabaklar veya herhangi bir nesne
grubunda çok kullanılana benzetirler. Mesela bir masanın üstünde sıra ile üst üste
konmus birden fazla tabaktan birisine ihtiyacımız olursa önce en üsttekini alırız. Bu
aldığımız tabakların en son konulanıdır.
Yığınların çalısma prensibini daha iyi
kavramak için yandaki canladırmayı
inceleyelim. Canlandırmada yığının
dısında bulunan sayıları yığının içine
koymak için "Yığını Doldur" düğmesine
tıkladığımızda sırası ile 17, 23, 4, 55 ve
8'i yığına sokuyor. Yığın doluyken "Yığını
Bosalt" düğmesine tıklayınca ise
yığındaki sayıların yerlestirildikleri sıranın
tersi sıra ile bosaltır. Bu durumda en son
giren 8 sayısı ilk önce, sonra 55, sonra 4
ve bu sıra ile en son 17 sayısı yığından
dısarı çıkar.
2. .NET Sınıf Kütüphanesi Yığın Sınıfı (Stack)
.NET sınıf kütüphanesinde yığın veri yapısını kullanmak için Stack sınıfını kullanırız.
Normalde C ve C++ dillerinde yığın veri yapısını değisik veri türleri ve kendi
tanımladığımız sınıflarla birlikte kullanmak zahmetli bir isti. (C'de sınıf kavramı yoktur!)
Ama .NET ortamında yığınlarımız bize birçok konuda esneklikler sunarak programlamayı
daha zevkli ve verimli hale getiriyor.
.NET'in yığın (Stack) sınıfını kullanmak için program kodunun bas tarafına using
System.Collections; eklememiz gerekir. Buradan yığın sınıfı System.Collections isim
alanında bulunuyor sonucunu da çıkartırız.
C# veya herhangi bir dilde yazılan yığın veri yapılarında Push(), Pop(), Peek() veya
Top(), Clear() fonksiyonları ve Count, özelliği temel olarak bulunur. Temel
fonksiyonların yanında Clone(), CopyTo(), ToArray(), Contains() ve Equals()
metodları .NET'in yığın sınıfında yeralır.
Yığın sınıfının Push() metodu yığına yeni bir eleman ekler. Pop() metodu ile yığının en
üstündeki elemanı yığından siler ve silinen elemanı geriye döndürür. Eğer yığının
tepesindeki elemanı öğrenmek istersek Peek() medodu isimize yarar. Bu metod yığının
tepesindeki nesneyi döndürür ama bu nesneyi yığından silmez.
using System;
using System.Collections; // Stack sınıfı bu isim alanı içinde bulunur.
class YiginSinifi1
{
public static void Main(string[] args)
{
// Stack sınıfından yigin nesnemizi tanımlıyoruz.
Stack yigin = new Stack();
// Yigini değisik değerlerde dolduruyoruz..
yigin.Push(12);
yigin.Push(5);
yigin.Push(23);
yigin.Push(34);
yigin.Push(70);
yigin.Push(8);
Console.WriteLine("Yığımızın ilk hali...");
ElemanlariYaz(yigin);
// Yigininin tepesinden bir sayı aldık
// ve bunu sayi değiskenine atayıp ekrana yazdıralım
int sayi = (int) yigin.Pop();
Console.WriteLine("\n Yığından {0} sayısını aldık", sayi);
// Yigininin tepesinden bir sayı daha aldık
// ve bunu sayi değiskenine atayıp ekrana yazdıralım
sayi = (int)yigin.Pop();
Console.WriteLine("\n Yığından {0} sayısını aldık", sayi);
// Simdi ise Yigininin tepesindeki sayıya bir bakalım
// bu sayıyı yığından çıkarmıyoruz.. Sadece ne olduğuna bakıyoruz..
sayi = (int) yigin.Peek();
Console.WriteLine("\n Yığının tepesindeki sayı su anda : {0}", sayi);
Console.ReadLine();
}
public static void ElemanlariYaz(Stack yigin)
{
object obj = new Object();
Stack yeniYigin = (Stack)yigin.Clone();
if(yigin.Count!=0)
{
while(yeniYigin.Count>0)
{
obj = yeniYigin.Pop();
Console.WriteLine("\t"+ obj.ToString());
}
}
else Console.WriteLine("Yığın bos...!");
}
}
Yukarıdaki programda önce Stack sınıfından yigin isimli bir nesne olusturuyoruz. Sonraki
altı satırda yığınımıza 12, 5, 23, 34, 70 ve 8 tamsayılarını Push() metodu ile ekliyoruz.
EkranaYaz() ismini verdiğimiz static fonksiyonumuz (bu fonksiyon tam olarak optimize
edilmis bir fonksiyon değil! ) ile yığınımızda bulunan elemanları ekrana yazdırıyoruz.
Daha sonra yığından iki tane elemanı Pop() metodu yardımıyla alıyor ve herbirini ekrana
yazdırıyoruz. Programın son kısmında ise Peek() metodunu kullanarak yığının en
üstündeki elemanın ne olduğunu öğreniyoruz.
Yığın sınıflarında bulunan diğer iki temel fonksiyonlar olan Count özelliği ve Clear()
metodlarıdır. Bunlardan Count, yığın nesnesinde bulunan elemanların sayısını geriye
döndüren bir özelliktir. Özellikler C# dilinde sınıflarda bulunan üye değiskenlerin
değerlerini öğrenmemize ve onların değerlerini değistirmemize yarayan bir tür
fonksiyonlardır. Count özelliği eleman sayısını int tipinde döndürür ve sadece okunabilen
(readonly) yapıdadır. Özellikler program içinde çağrılırken parantezleri kullanmayız. Eğer
yigini bosaltmak/temizlemek istersek Clean() metodu isimizi yarayacaktır. Clean()
metodu hiçbir parametre almaz ve hiçbir sey döndürmez. Herhangi bir yığın 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öndürür. Đsterseniz asağıdaki
programda Contains() ve Clear() metodları ile Count özelliklerini nasıl
kullanabileceğimizi görelim:
using System;
using System.Collections; // Stack sınıfı bu isim alanı içinde bulunur.
class YiginSinifi1
{
public static void Main(string[] args)
{
// Stack sınıfından yigin nesnemizi tanımlıyoruz.
Stack yigin = new Stack();
// Yığınımıza yeni elemanlar ekliyoruz.
yigin.Push("Ahmet");
yigin.Push("Sefer");
yigin.Push("Cemal");
yigin.Push("Onur");
yigin.Push("Aziz");
// Yığında kaç tane eleman bulunduğunu bulup yazalım.
int elemanSayisi= yigin.Count;
Console.WriteLine("\nYığınımızdaki eleman sayısı: {0}", elemanSayisi);
// Yığındaki elemanlar.
Console.WriteLine("\nYığındaki elemanlar: ");
ElemanlariYaz(yigin);
//Contains() metodunun kullanımı:
if(yigin.Contains("Sefer"))
Console.WriteLine("\nYığında Sefer elemanı var...");
else
Console.WriteLine("\nYığında Sefer elemanı yok...");
// Yığını bosaltalım.
yigin.Clear();
// Yığını bosalttıktan sonra kaç tane eleman bulunduğunu bulup yazalım.
elemanSayisi= yigin.Count;
Console.WriteLine("\nYığınımızdaki eleman sayısı: {0}", elemanSayisi);
Console.ReadLine();
}
public static void ElemanlariYaz(Stack yigin)
{
object obj = new Object();
Stack yeniYigin = (Stack)yigin.Clone();
if(yigin.Count!=0)
{
while(yeniYigin.Count>0)
{
obj = yeniYigin.Pop();
Console.WriteLine("\t"+ obj.ToString());
}
}
else Console.WriteLine("Yığın bos...!");
}
}
Hemen üstteki programdan önceki programda yığınımıza int tipinden nesneler (c#'ta
primitive türler dahil hersey nesnedir!) yerlestirmistik. Bu örnekte ise string sınıfına ait
nesneleri yığınımıza ekledik ve onlar üzerinde islemler yaptık. Yani yığın sınıfımız
herhangi bir nesneyi tutabilecek yetenekler sabit. Đster temel veri türleri olsun (int, byte,
double veya bool) ister kendi tanımladığımız veri türleri olsun yığın sınıfımıza ekleyip
çıkartabiliriz.
Yukarıdaki programda yigin olarak olusturduğumuz ve yığın sınıfındaki nesnemize bes
tane veriyi ekliyoruz. Sonra yığında kaç tane eleman olduğunu bulmak için Count
özelliğinden faydalanıyoruz. Yığındaki elemanları yazdırmak için ElemanlariYaz() sabit
fonksiyonumuzu kullanıyoruz. Contains() metodunu kullanımına örnek olması amacıyla
if deyimi içinde yigin.Contains("Sefer") sorgusunu yapıyoruz. Eğer Sefer elemanı yığında
mevcutsa ekrana yığında olduğunu, yoksa yığında olmadığını yazdırıyoruz. Programın
geriye kalan kısmında yığını bosaltmak için Clear() metodunu kullanıyoruz,yığındaki
eleman sayısını tekrar bulup bunu yazdırıyoruz. Eğer tekrar ElemanlariYaz()
fonksiyonunu kullansaydık. "Yığın Bos..!" uyarısını alırdık!
.NET sınıf kütüphanesinde bulunan Yığın (Stack) sınıfının getirdiği kolaylıklar
yukarıdakilerden daha fazladır. Mesela herhangi bir yığın nesnemizi baska bir yığının içine
kopyalayabiliriz. Bir yığını baska bir yığına kopyalamak için Clone() metodunu
kullanabiliriz. Ayrıca bir yığın nesnesini herhangi bir dizinin içine kopyalamak için
ToArray() metodu kullanılabilir. Đki tane yığının birbirlerine esit olup olmadığını
öğrenmek için Equals() metodu hemen yardımımıza yetisir. Burada sunu belirtmekte
yarar var: Equals() metodu sanal (virtual) bir fonksiyon olup c#'daki tüm nesnelerin
türediği System.Object nesnesine aittir.
Bu makalede inceleyeceğimiz son program asağıdadır. Bu programla Clone(), Equals()
ve ToArray() metodlarını programlarımız içinde ne sekilde kullanacağımızı öğrenebiliriz.
using System;
using System.Collections; // Stack sınıfı bu isim alanı içinde bulunur.
class YiginSinifi1
{
public static void Main(string[] args)
{
// Stack sınıfından yigin nesnemizi tanımlıyoruz.
Stack yigin1 = new Stack();
// Yığınımıza yeni elemanlar ekliyoruz.
yigin1.Push("Ahmet");
yigin1.Push("Sefer");
yigin1.Push("Cemal");
yigin1.Push("Onur");
yigin1.Push("Aziz");
//Đkinci yığınımızı tanımlıyor ve yigin1'in
// bir kopyasını yigin2'ye koyuyoruz..
Stack yigin2= (Stack)yigin1.Clone();
// yigin1'den bir eleman çıkartıyoruz.
yigin1.Pop();
//yigin1 ve yigin2 nesnelerimizin en üstteki
// elemanlarına bir bakalım:
Console.WriteLine(" Peek of Yığın2: "+ yigin2.Peek());
Console.WriteLine(" Peek of Yığın1: "+ yigin1.Peek());
//yigin1 ve yigin2 esit mi? Bir bakalım:
Console.WriteLine("\n yigin1 ve yigin2 esit? --> "+ yigin1.Equals(yigin2));
//yigin2'yi kopyalamak için yeni bir dizi olusturalım:
Array arr = new Array[5];
// yeni olusturduğumuz diziye yigin2'yi kopyalayalım:
arr = yigin2.ToArray();
// arr nesnesinin elemanları:
Console.WriteLine( "\n\n arr nesnesinin elemanları:\n"+
"\n\t"+arr.GetValue(0)+
"\n\t"+arr.GetValue(1)+
"\n\t"+arr.GetValue(2)+
"\n\t"+arr.GetValue(3)+
"\n\t"+arr.GetValue(4) );
Console.ReadLine();
}
public static void ElemanlariYaz(Stack yigin)
{
object obj = new Object();
Stack yeniYigin = (Stack)yigin.Clone();
if(yigin.Count!=0)
{
while(yeniYigin.Count>0)
{
obj = yeniYigin.Pop();
Console.WriteLine("\t"+ obj.ToString());
}
}
else Console.WriteLine("Yığın bos...!");
}
}
Yazımızda bilgisayar programlama alanında en önemli veri yapılarından biri olan yığınların
(Stack) nasıl çalıstıklarını ve .NET sınıf kütüphanesinde bulunan Stack sınıfını ve
metodlarının nasıl isimize yarayacak sekilde kullanabileceğimizi öğrendik. Umarım bu
yazının size gerçek manada yararı olur.
C#'ta Gösterici(Pointer) Kullanmak – I
Göstericiler(Pointer) alt seviye programlama için olmazsa olmaz yapılardır. Göstericiler
nesnelerin bellekte tutuldukları adresleri saklayan veri yapılarıdır. Bu makalede C#'ta
kullanımı çok fazla gerekli olmayan göstericilerin nasıl kulanıldıklarını inceleyeceğiz. Bu
yazı göstericiler hakkında temel bilgilere sahip olduğunuzu varsaymaktadır.
.NET' in altyapısında gösterici kavramı sıklıkla kullanılırken göstericilerin açıkca kullanımı
programcılar için gizlenmitir. Bunun nedeni gösterici kullanımının programlardaki
görülemeyen hatalara sıkça yol açabilmesidir. Özellikle dili yeni öğrenenler için gösterici
hataları içinden çıkılmaz bir hale gelebilmektedir. C#'ta göstericiler yerine referans
değiskenleri mevcuttur. Referanslar heap bellek bölgesindeki nesnelerin baslangıç
adresini tutar. Ancak bu adresin değerine kesinlikle ulasamayız. Oysa C ve C++ dillerinde
stack bölgesindeki değiskenlerin de adreslerine erisbilmemiz mümkündür. Üstelik
değiskenlerin adreslerini örneğin 0x965474 seklinde elde etmemiz bile mümkündür. C ve
C++ programcılarına pek yabancı gelmeyecektir bunlar, ancak programlama dünyasına
C# ile giren biri için göstericilere neden ihtiyaç duyabileceğimiz pek anlamlı gelmeyebilir.
Sunu da söyeleyelim ki çok istisnai durumlar dısında göstericilere ihtiyacımız olmayacak,
peki bu istisna durumlar nelerdir?
• Geriye Uyumluluk(Backward Compatibility) : COM ve WinAPI'deki
fonksiyonlar gibi sık sık gösterici kullanan fonksiyonları C# ile programlarımızdan
çağırabilmek için parametre olarak gösterici alan fonksiyonlara gösterici
göndermemiz gerekebilir. Eğer C# ta gösterici kullanımına izin verilmemis olsaydı
tür uyumsuzluğu yüzünden gösterici kullanan eski COM ve DLL'lere erisebilmemiz
mümkün olamazdı.
• Performans : C ve C++ dillerinin günümüze kadar çok popüler olmasının altında
yatan nedenlerden biri de belleğe direkt erisimi sağladığı içinperformansın
inanılmaz derecede yükselmesidir. Bizim için performansın çok önemli olduğu
yerlerde gösterici kullanmamızdan daha doğal birsey olamaz.
• Alt Seviye Đslemler : Donanım arayüzleri ile direkt bir iliski içerisinde olacak
programlarda göstericilerin kullanımı mecburi gibidir. Bazen de belleğe
kullanıcıların direkt erisebilmesi gereken programlar olabilir. Bu durumlarda da
göstericilerden faydalanabiliriz.
Bütün bunların yanında gösterici kullanmanın bir çok sıkıntıyıda beraberinde getireceği
kesindir. Öncelikle kullanımının zor olması ve kestirilmesi zor olan hatalara yol açabilmesi
bu sıkıntıların en basında gelenidir. Zaten C#'ta gösterici kullanacağımız zaman kodu
unsafe(güvensiz) anahtar sözcüğü ile isaretlememiz gerekir. Aksi halde program
derlenemeyecektir. Normal bir metot içinde gösterici kullanımı yasaklanmıstır.
Simdi de unsafe anahtar sözcüğünün kullanımına örnekler verelim.
1-) unsafe olarak isaretlenen sınıfların bütün metotlarında gösterici kullanabiliriz.
unsafe class Sınıf
{
}
2-) Normal bir metot içinde herhangi bir bloğu unsafe olarak asağıdaki gibi isaretleyip
dilediğimiz gibi gösterici kullanabiliriz. unsafe bloklarının dısında ise gösterici
kullanamayız.
int NormalMetot(int a, string str)
{
unsafe
{
}
}
3-) Normal bir metodu unsafe olarak isaretleyip sadece o metodun içinde de gösterici
kullanabiliriz.
unsafe int NormalMetot(int a, string str)
{
}
4-) Bir sınıfın üye değiskenlerinden biri unsafe olarak isaretlenip gösterici olarak
bildirilebilir. Ancak bir metot içerisinde yerel bir gösterici tanımlanamaz. Yerel bir
gösterici tanımlamak için unsafe olarak isaretlenmis metod yada blok kullanılır.
class Sınıf
{
unsafe char *ptr;
}
Gösterici Tanımlama ve Gösterici Operatörleri
Göstericiler asağıdaki gibi tanımlanabilir.
char* ptr1, ptr2;
int* ptr3;
ptr1 ve ptr2 char türden bir gösterici iken ptr3 int türden bir göstericdir. Bir göstericide
iki bilesen vardır. Bu bilesenlerden birincisi adres bilesenidir. Adres bileseni nesnenin
bellekte bulunduğu baslangıç adresidir. Đkinci bilesen ise tür bilesenidir. Bu bilesen ise
ilgili adresteki nesneye ulasmak istediğimizde bellekten ne kadarlık bilgili okunacağını
sağlar. Örneğin int türden bir adresteki bilgiyi okumak istediğimizde 4 byte'lık bir bilgi
okunacaktır, aynı sekilde char türden bir gösterici ise 2 byte'lık bir bilgi okunacaktır.
& operatörü
Adres operatörü olarak bilinen bu operatör değiskenlerin veya nesnelerin bellekte
bulundukları adresleri elde etmek için kullanılır. Bu operatör hangi tür değiskenle
kullunılırsa o türden bir gösterici üretilir.
* operatörü
Đçerik operatörü olan *, bir adresteki bilgileri elde etmek için kullanılır. Asağıdaki
programda bu iki operatörün kullanımına bir örnek verilmistir.
using System;
class Class1
{
unsafe static void Main()
{
int* ptr1;
char* ptr2;
int a = 50;
ptr1 = &a;
int Adres = (int)ptr1;
Console.WriteLine("{0:X}",Adres);
char ch = 'A';
ptr2 = &ch;
*ptr2 = 'B';
Console.WriteLine(ch);
}
}
Bu programı derleyebilmek için komut satırı derleyicisene programın içinde unsafe
bloğunun olduğunu belirtmemiz gerekir. Bunun için komut satırına
csc /unsafe KaynakKod.cs
yada
csc -unsafe KaynakKod.cs
yazarak programı derleyebilirsiniz. Eğer Visual Studio.NET kullanıyorsanız projeye sağ
tıklayıp proje özelliklerine gelip Build kısmından "Allow unsafe code blocks" kısmını true
olacak sekilde değistirin.
Programı çalıstırdığınızda ekrana a değiskeninin adresi ve B karekteri yazılacaktır. Adres
bilgisini ekrana yazdırmadan önce göstericide tutulan adresi normal bir türe nasıl
dönüstürdüğümüze dikkat edin. Bunu yapmamızın sebebi Consol.WriteLine() metodunun
gösterici parametres alan versiyonunun olmamasıdır.
C#'ta tür güvenliğinin ne kadar önemli olduğunu vurgulamak için asağıdaki deyimleri
örnek verebiliriz.
int* ptr1;
*ptr1 = 50;
Bu deyimleri içeren bir program derlenemeyecektir. Çünkü ptr1 göstericisinde hangi
adresin tutulduğu belli değildir. Bu yüzden adresi belli olmayan bellek bölgesine bir değer
yerlestirmek imkansızdır. C ve C++ dillerinde bu kullanım tamamen geçerlidir. Ama gelin
bir de bunun sakıncasına bir göz atalım. ptr1 göstericisi tanımlandığında ptr1 de rastgele
bir adres değeri bulunmaktadır. Bu adreste rastgele bir adres olduğu için o an bellekte
çalısan kendi programımızdaki bir değiskenin adresi bile olabilir. Yada sistemde çalısan
baska bir prosesteki elemanların adresleri olabilir. Kaynağını bilmediğimiz bir adresteki
değeri * operatörü ile değistirdiğimizde hiç tahmin edemeyeceğimiz çalısma zamanı
hataları alabiliriz. Đsin kötüsü adresler rastgele olduğu için programımızın test asamasında
bu hatalar olusmayabilir. Adresler nasıl rastgele ise hataların olusması da rastgeledir.
Programımız piyasada iken böyle bir hatanın farkına varılması is maliyetlerini ne kadar
artırdığını siz tahmin edin artık. Bütün bu dezavantajlar göstericilerin asla gereksiz olduğu
anlamına gelmemelidir. Sadece göstericileri kullanırken daha dikkatli davranmamız
gerektiğinin göstergesidir.
Göstericiler arasında tür dönüsümleri mümkündür. Örneğin int türden bir gösterici char
türden bir göstericiye asağıdaki gibi dönüstürülebilir.
char* ptr1
int* ptr2;
ptr1 = (char*)ptr2
Aynı sekilde bir gösterici de tamsayı türlerine dönüstürülebilir. Adresler tam sayı
türünden birer sayı oldukları için bunu yapabilmemiz son derece normal bir durumdur. Bir
önceki çalısan programda da Console.WriteLine() metodu ile ekrana yazmak istediğimiz
nesnenin adresini tamsayı türlerinden birini çevirdiğimizi hatırlayın. Örneğin asağıdaki
deyimleri içeren bir program derlenemeyecektir.
char* ptr1
char ch = 'A';
ptr1 = ch;
Console.WriteLine(ptr1)
Göstericilerle ilgili diğer önemli yapı ise void göstericilerdir. void olan göstericilerde tür
bilgisi saklanmamaktadır. void göstericilere herhangi bir türden gösterici atanabilir. void
göstericiler daha çok eskiden yazılmıs API fonksiyonlarında void parametre alan
fonksiyoları programlarımız içerisinden çağırmak için kullanılır. void göstericilerin
kullanımına asağıda bir örnek verilmistir.
int* ptr1;
void * x;
x = ptr1;
sizeof operatörü
sizeof operatörü temel türlerin ve yapıların bellekte ne kadar alan kapladıklarını verir.
Örneğin sizeof(int) = 4, sizeof(char) = 2 ' dir. sizeof operatörünü sınıflar için
kullanamayız ancak tanımlayacağımız yapılar için kullanabiliriz.
Bu yazının sonuna geldik. Bir sonraki yazımızda yapı göstericileri tanımlamayı sınıflar ile
göstericiler arasındaki iliskiyi, gösterici aritmetiğini inceleyip örnek bir gösterici
uygulaması yapacağız.
Kaynaklar:
C# and The .NET Platform (Andrew Troelsen)
Professional C# 2nd Edition (Wrox)
MSDN Kütüphanesi
C#-JAVA' nın Sonu mu?
JAVA'nın gün geçtikçe yaygınlasan kullanım alanları, Microsoft firmasını gerçekten güç
duruma düsürdü. 1995 yılında JAVA ufukta göründü ve geçtiğimiz 6 yıl içerisinde kendisi
ile rekabet eden diğer bütün popüler diller üzerindeki üstünlüğünü gösterdi. Baslangıçta
Microsoft bu duruma direndi ancak daha sonra, JAVA meshur Visual Studio nun Visual
J++ adı altında bir parçası oldu. JAVA' yı çıkaran Sun Microsystems, Microsft aleyhine
lisans anlasmasını ihlal ettiği gerekçesiyle dava açtı ve Microsft buna karsılık bir bedel
ödemek zorunda kaldı.
Sonunda ortalık yatıstı ve Microsft Visual J++ 'ın yeni versiyonlarını çıkarmaya devam
etti. Fakat Microsoft'un JAVA dilini Visual Studio paketinin içine koyma çabası basarıya
ulasamadı ve en sonunda basarız oldu. Elbette ki bu basarısızlık ta pazarlamanın iyi
yapılamamasının da etkisi olmustur. Sonuç olarak Microsoft 1998 yılından sonra Visual
J++ için yeni bir versiyon çıkarmadı, ve yakın bir gelecek içinde de bu tür planlarını
askıya aldı.
Sonunda Microsft anladı ki bir yerlerden ödünç alınan ürünler uzun vadede ise
yaramayacak, bu yüzden Microsoft, daha çok JAVA'ya benzeyen ancak JAVA da olmayan
bir takım yeni özellikleride ekleyerek C# (C Sharp) isimli yeni bir programlama dili
tasarlamaya basladı. Bu makalenin amacı C# hakkındaki süphelerinizi gidermek ve JAVA
ile karsılastırmaktr.
Peki, Microsoft 'un ilgilendiği bu pakette önerdikleri nelerdi?
Bu pakette Microsoft'un önerdiği iki ana yapı vardı:
.NET Mimarisi
Yeni bir dil - C#
.NET Mimarisi
.NET mimarisinin 3 ana bileseni vardır.
Herhangi bir dil tarafından kullanalabilecek Web Servisi denilen yeni bir ortak servisler
kümesi. Bu servisler ara kod(intermediate kod) dediğimiz mimariden bağımsız bir yapı
içerisinde çalıstırılır. Bu ara kod daha sonra çalısma zamanında CLR denilen birim
tarafından çalıstırılır, ki bu bahsedilen CLR birimi programla ilgili kaynakları yöneten ve
programın çalısmasını izleyen bir birimdir.
.NET'in birincil amacı gelistiriciler açısından Web Servislerini kullanarak mimariden
bağımsız uygulamalar gelistirmeyi kolaylastırmaktır. Örneğin bir servise hem PC hem PDA
hemde Mobil cihazlardan erisebilme gibi.
Simdi bu yapıları tek tek inceleyelim.
Web Servisleri
Web Servisleri internet standartları olan XML ve HTTP üzerinden hizmet verebilen
yapılardır. Daha teknik bir dille söylemek gerekirse, web servisleri internet üzerinden
erisilebilen bir kütüphane, bir fonksiyon yada bir veritabanı gibi düsünülebilir. Örneğin;
bir web servisi herhangi bir sirkete ait bir yıllık ciroyu hesaplayıp veren bir fonksiyon
içerebilir. Bu fonksiyon parametre olarak bir yıl değeri almaktadır. Dolayısıyla istemci
uygulamamız geri dönüs değeri 2000 yılına ait ciroyu Yıllık_ciro(2000) gibi bir fonksiyonu
çağırarak elde edebilir.
Aslına bakarsanız bir firmada farklı platformlara yönelik hazırlanmıs uygulamalar arasında
ki senkronizasyonu sağlamak amacıyla Web Servisleri kullanılabilir. Buna rağmen Web
Servisi kelimesindeki "Web" sözcüğü ile internet ortamında web servislerinin kullanımını
ön plana çıkarmak için kullanılmıstır. Fakat normal ugulamarda da çoğu durumda Web
servsilerinden faydalanmamız mümkündür.
Web servislerinin en güzel yani servisin her yerden istenebilmesidir. Kısacası web servisi
uygulama alanlarına Windows, Linux, Unix, Mac-Os gibi platformların yanısra komut
satırından çalısan diğer ortamlar bile girebilir. Yani Web servisleri sayesinde platform
bağımsız uygulamalar gelistirebiliriz. Đnternet standartlarının platform bağımsız olması ve
web servislerininde internet üzerinden çağrılması, web servislerinin herhangi bir isletim
sisteminin yada programlama dilinin tekelinde olmamasını sağlamıstır.
Ara Kod(Đntermediate Code)
Ara kod! Kulağa pek hos gelmiyormu? Bu bana birseyi hatırlatıyor....hımmm..... JAVA?
Evet, elbette. Microsoft açık bir sekilde .NET ile JAVA' nın çalısma platformundan
esinlendiğini kabul etti böylece. .NET ortamında, programlar bir ara dil üzerinden ikili
koda çevrilir. Đsletim sisteminden ve donanımdan bağımsız olan bu dile MSIL(Microsft
Intermediate Language) denir. (Çevirenin notu: MSIL artık IL olarak anılmaktadır.) IL
kodu, JAVA platformundaki JVM de olduğu gibi CLR tarafından derlenip makina koduna
çevrilir. IL kodunu makine koduna çeviren derleyicilere ise JIT(Just In Time) derleyicileri
denilmektedir.
IL kodu seklinde derlenmis bir uygulamaya tasınabilen çalıstırılabilir(Portable
Executables) uygulama denir. CLS nin sartı bütün dillerin ortak bir noktada toplanmasını
sağlayacak kurallar olusturmaktır. Ancak burada bir problemle karsılasıldı, oda bütün
dillerin farklı semantik yapıya sahip olmasıdır, örneğin her dilin farklı özellikleri olabilir,
bazı diller prosedürel tekniği destekler, bazıları nesne yönelimi tekniğini destekler,
bazılarında operatör yükleme varken bazılarında yok, ve diğerleri.
Bir dilin .NET uyumlu olması için CLS(Common Language Specification) standartlarına
uyması gerekir. Dolayısıyla bir dilin .NET ortamına uyumlu olması için sağlaması gereken
iki kosul vardır:
CLS ile uyumluluk
Kaynak kodu IL koduna çevirecek derleyici
Peki yukarıda bahsedilen komplek yapı bize ne kazandıracak? Diller arası çalısma? Bütün
.NET uyumlu dillerde yazılmıs kodlar ara koda çevrileceği için herhangi bir dil de yazılmıs
bir nesneyi baska bir dilde yazdığımız programda olusturabiliriz. Örneğin .NET uyumlu C#
dilinde yazılmıs bir sınıf ile, VB programcısı bir nesne olusturabilir.
Peki JAVA'nın bu durumdaki rolü ne? Yada Microsoft JAVA yı devre dısımı bıraktı? Tam
olarak değil. JAVA nın .NET dilleri arasında olması düsünüldü. JAVA kodunu IL koduna
çevirecek derleyici Rational firması(ünlü UML yazılım gelistirme aracını gelistiren firma)
tarafından hazırlanmaktadır.(Not: bu yazının yaıldığı tarihte JAVA derleyicisi yeni
yazılıyordu). Peki bu eskisiyle aynı JAVA mı olacak? Aslına bakarsanız hayır. JAVA kodları
artık byte kod yerine IL koduna çevrilecek.Yeni JAVA, J2EE'nin sağladığı RMI, JMS, JDBC,
JSP vs gibi API' lerden faydalanamayacak.Yeni gelistirilen JAVA ile EJB mantığı yerini
.NET'in dağıtık nensne modeline bırakmıstır.
CLR(Common Language Runtime)
Yukarıda da bahsettiğim gibi CLR, konsept olarak JVM(Java Virtual Machine)'ye çok
benzemektedir. JVM bellek yönetimi için kaynakların düzenlenmesi, gereksiz bilgi(çöp
bilgi) toplama ve isletim sistemi ile uygulama arasındaki iletisimi sağlayan bir birimdir.
CLR, dizilerin sınırlarının asılması, tahsis edilmeyen bellek alanına ulaslması, bellekteki
gerekli alanların üzerine yazılması gibi programların çökmesine sebep olan geleneksel
hataların durumunu izler. Bu izleme hızlılıktan fedakarlık sağlamak demektir. Microsoft bu
yöntemle performansın %10 oranında düseceğini kabul etmistir, fakat bu sistemler
devamlılık ve güvenilirlik açısından daha ön plandadır.
.NET Senaryoyu Sunuyor
Bu yılın ortalarına doğru .NET'in tam sürümü piyasada olacak(Çevirenin not: Yazının
yazıldığı tarihe göre düsünmelisiniz) Microsoft mimarisinin kullanıldığı eski uygulamalar
.NET uyumlu Windows 2000 sunucularında hala geçerliliğini sürdürecek. Eski mimarideki
uygulamaların yeni gelecek bu mimariye tasınabilmesi için gerekli araçların olacağını
Microsft garanti etmistir, ancak bunun iyi bir çözüm olacağı görülmüyor. Halihazırdaki
tasımak için gelistirilen araçların bu islemi %100 yapamayacağı açıktır. Gelistiriciler bu
tasıma isleminden dolayı epeyce bir zaman harcayacak.
.NET'in Geleceği
.NET'in gelisi ile birlikte, elektronik uygulamaların altyapısının değismesi gerektiğini inkar
edemeyiz. Her dil, .NET uyumlu olması için yapısal bir takım değisiklikler yapmalıdır. Java
dilini bir düsünün. Hangi Java daha tercih edilir sizce, JVM ile JAVA mı yoksa .NET ile
JAVA mı? Bu sorunun cevabını siz okurlarıma bırakıyorum.
Yeni Bir Dilin Doğusu
Microsft .NET'in 27 farklı dil desteğinin olduğunu iddia ediyor.(Çevirenin not: Su an bu
dillrein sayısı daha fazla) Bir dili .NET uyumlu hale getirmek dilin varolan mimarisine yeni
yamalar eklemek demektir. Microsft bu durumu değerlendirerek temel olarak .NET
mimarisi hedef alınarak yeni bir dil tasarladı. Bu dilin adı C-Sharp yada C#. Bu dili daha
çok JAVA yı andırıyor olmasına rağmen var olan diğer dillerden de güzel özellikler alınıp
C# diline eklenmis gibi gözüküyor.
C# nedir?
Geçen iki yüzyıl içerisinde C ve C++ dilleri ticari ve is uygulamaları gelistirmek için en çok
kullanılan diller olmustur. Bu iki dil programcılara büyük miktarda kontrol sağlamasına
rağmen, üretim açısından çok olanak sağlamıyordu.
Bu dilleri Visual Basic gibi dillerle karsılastırdığımızda aynı uygulamyı gelistirmenin C ve
C++ dillerinde daha fazla zaman aldığı bir gerçektir. Yıllardan beri istenilen tek sey
güçlülük ve üretim hızının arasında bir denge kuracak bir dilin olmasıydı. Geçtiğimiz
yıllarda bu problem Sun Microsystems tarafından anlasıldı. Ve diğer dillerin çalısma
mantığından tamamen farklı olan JAVA dilini gelistirdi. Bytecode konsepti, JVM ve açık
kaynak kod vs ve sonuç olarak bedeva olması bu farklılıklardan bir kaçıdır.
Fakat unutulan bir sey vardı. JAVA ile yazılan kodlar baska dillerde kullanılamıyordu.
Tekrar kullanılabilirlik denen bu özelliği JAVA desteklemiyordu. Böylece, tekrar
kullanılabilirliği standartlastıracak bir ortamın gerekliliği ortaya çıktı. Bu ortam .NET ile
sağlandı, zira .NET ile uyumlu dillerin sağlaması gereken CLS standartları mevcuttur.
Fakat daha öncede dediğim gibi .NET ile tam uyumlu çalısacak ve diğer dillerin güzel
özelliklerini sağlayacak yeni bir dile ihtiyaç vardı.
Hakkında konustuğum bu dil C-Sharp diye okunan C#'tan baska birsey değildir.
C#, modern, nesne yönelimli, tip güvenliğine büyük önem veren, temel hesaplama ve
haberlesme gibi genis bir yelpazedeki uygulamaları gelistirmek için .NET platformu için
tasarlanmıs yeni bir dildir.
C#, C++ programcılarınına üretkenliği ve C++ dilinin güçlülüğünün göstergesi olan
kontrol mekanizmalarından taviz vermeden hızlı bir sekilde uygulama gelistirme olanağı
tanımaktadır.
C# ve JAVA'nın Karsılastırılması
Đyi bir dilin sağlaması gereken seyler nelerdir? Yada programcıların gönlünü kazanmak
için bir dil yapısında neyi barındırmalıdır? Bu sorular on yıllardan beri dil tasarımcılarının
üzerinde durduğu tartısmaya açık sorulardır. Bütün programcılar tarafından ittifakla kabul
edilmistir ki, bu soruların cevabını en iyi sekilde veren dil JAVA'dır. C#, sentaks olarak
JAVA'ya çok benzemektedir, ancak derinlerine daldıkça Microsoftun bu dili tasarlamak için
çok efor sarfettiğini ve bu yeni eklenen bu özellikler için Microsoft'a tesekkür etmemiz
gerektiğini anlarız.
Simdi isterseniz JAVA nın özelliklerini tek tek ele alıp bunları C#'ın ilgili özellikleriyle
karsılastıralım.
Intermediate Language(Ara dil)
JAVA kaynak kodu byte koduna çevirirken C# MSIL(IL) koduna çevirir. IL dili .NET
uyumlu bütün dillerde yazılmıs olan programların ortak olarak derlendiği dildir. Fakat IL
ve Byte Kod'un çalısma mantığında ince bir farklılık vardır. JAVA daki bytecode'lar
yorumlanırken IL kodu derlenerek makina koduna çevrilir.
Interoperability (Diller arası uyumluluk)
JAVA'nın gücü platform bağımsız olmasından kaynaklanmaktayken C# aynı zamanda
diller arasındaki uyumluluğuda sağlar. Yani dil bağımsızlığı. Güzel!. Simdi bunları biraz
daha açalım: JAVA ile yazılmıs bir program JVM'nin olduğu bütün sistemlerde çalısması
sözkonusu iken, C# ile yazılan bir kod diğer .NET uyumlu diller tarafından tekrar
kullanılabiliyor. Örneğin bir dilde yazılan sınıf, diğer .NET uyumlu diller ile rahatlıkla
kullanılabilir.
Bellek Yönetimi
JAVA otomatik bellek yönetimi sağlamaktadır.(daha teknik bir deyimle gereksiz bilgi
toplama mekanizması denir.) Bu özellik programcılar tarafından takdirle karsılanmıstır.
Fakat eski C/C++ programcıları JAVA diline geçmeye çalısınca bu özellik onları rahatsız
ediyordu. Bu tür programcıların problemlerini da göz ardı etmeden, C# otomatik bellek
yönetiminin yanında programcının belleği kendisininde yönetmesini sağlayan sistem
sunmustur. Ne demek bu? Basit. C#' ta hala pointer kullanabiliyoruz. (Çevirenin Notu:
Müthis!)
Harici Kodlara Referans
Harici kodlar C# ve JAVA'da benzer sekilde ele alınmıstır. JAVA dilinde import anahtar
kelimesi kullanılırken C# ta using anahtar kelimesi kullanılmaktadır. JAVA
paket(packages) kullanırken, C# isim uzayları(namespace) kullanır. Fakat bunların
anlamları asağı yukarı aynıdır.
Veri Tipleri
Bir dilin gücü, dilin desteklediği farklı veri türleri tarafından belirlenir. Veri tipleri
programcılara güçlülüğü ve esnekliği sağlayan varlıklardır. C#, JAVA daki bütün veri
tiplerini sağlamıstır, bunun yanısıra JAVA da olmayan bazı türler de eklenmistir, örneğin
bazı isaretsiz(unsigned) veri türleri JAVA da yoktur. Ayrıca C# ta kayan noktalı(floating
point) bir veri türü olan 12 byte'lık decimal türü de mevcuttur.
Alan Düzenleyicileri (Field Modifiers)
C# taki Alan düzenleyicileri temel olarak JAVA dilindeki gibidir. Değistirlemeyen yada
sabit bir değisken tanımlamak için JAVA daki final dan farklı olarak read only ve const
belirleyicileri kullanılır. const olan alan düzenleyiciler, ilgili değerin IL kodunun bir parçası
olduğu ve sadece çalısma zamanında hesaplanacağı anlamına gelir.
Kontrol Mekanizması Olusturma
if-else, switch, while, do-while, for, break, contine deyimleri her iki dilde aynıdır. Fakat
C# ta yeni bir kontrol daha vardır. C# taki bu yeni yapı koleksiyonlar arasında dolasmak
için gerekli olan for each yapısıdır.
int slist(Arraylist alist)
……….
foreach (int j in alist)
{
………….
}
Yukarıdaki yapıda j döngü değiskeni olarak adlandırılır. Bu döngü değiskenine her
iterasyonda alist dizisinin int türden olan elemanı atanır.
Arayüz ve Sınıf Bildirimi
JAVA daki extends ve implements anahtar sözcükleri C# te yerini iki nokta isaretine(:)
bırakmıstır.
Đstisnai Durumları Ele Alma(Exception Handling)
C# ta catch bloğundaki argüman isteğe bağlıdır. Eğer catch ile argüman belirtilmemisse,
bu catch bloğu try bloğunda fırlatılacak herhangi bir hatayı yakalamak için kullanılır.
Bütün catch bloğuda C# ta kullanılmayabilir. Ayrıca C# ta throws(çevirenin not: throw ile
karıstırmayın) anahtar sözcüğü yoktur.
Arayüzler
C# ta bir sınıf isteğe bağlı olarak açıkca bir arayüzü uygulayabilir. Açıkca uygulanan
metotlar arayüz ve sınıf arasındaki tür dönüsümü sayesinde çağrılabilir.
Kalıtım
JAVA ve C# ta sadece tekli türetme mevcuttur. Eğer çoklu türetme yapmak gerekiyorsa
arayüzleri kullanmak tek yoldur.
Çok Biçimlilik(Polymorphism)
Sanal metotlar çok biçimliliği gerçeklestirmek için kullanılır. Bunun anlamı taban sınıfların,
türemis sınıflara ait asırı yüklenmis metotları çağırabilmesidir. JAVA da bütün metotlar
sanaldır, fakat C# ta türemis bir sınıftaki metodu taban sınıf ile çağırabilmek için
metodun açıkca virtual anahtar kelimesi il isaretlenmesi gerekir. C# ta override anahtar
kelimesi bir metodun türeyen sınıfta yeniden yazılacağını bildirmek için gereklidir. Sanal
olmayan bir metodu yeniden uygulamaya çalısmak derleme zamanı hatasına yol
açacaktır. Fakat eğer türemis sınıftaki metot new anahar sözcüğü ile isaretlenirse
program hata vermeden derlenecektir.
Pekala, sonuç nedir? C# te yeni bir sey bulabildinizmi, yoksa JAVA nın üvey kardesi gibi
mi duruyor? Microsoft bu uğrasının karsılığını ilerde alacakmı? Yeni bir dilin var olan
uygulamalar üzerinde, değisik platformlarda hatta programcılar üzerindeki etkisi ne
olacak? Bu can alıcı soruların cevabını ancak zaman verebilir. Fakat bu arada beyninizde
bu soruları yavas yavas çözün ve düsünün. C# gerçekten JAVA nın sonumu?
----------------------------
Bu makale http://www.csharphelp.com/archives/archive86.html adresindeki yazıdan
Türkçeye tercüme edilmistir.
C# ile Zamanlayıcı Kullanmak (System.Timers)
Windows programlama ile uğrasanlar Timer kontrolünü sık sık kullanmıslardır. Bu yazıda
System.Timers isim alanında bulunan Timer sınıfı ile bir programdaki rutin olarak
yapılması gereken isleri belirli bir zaman aralığı içinde ne sekilde yapabileceğimizi
öğreneceğiz. Ancak alısalıgelmis sekilde bir grafik arayüzü olan program yerine
uygulamamızı komut satırından çalısacak sekilde gelistireceğiz. Burdaki amacımız
zamanlayıcı dediğimiz timer nesnelerinin çalısma prensibini yakından görmektir.
Söyle bir konsol uygulaması yapmanız gerektiğini düsünün. Her 5 dakikada bir, belirli bir
kaynaktaki değisiklikler kontrol edilecek. Eğer kaynakta bir değisiklik varsa kullanıcıya
uyarı verilecek. Bunu klasik yöntemlerle ne sekilde yapabilirdiniz bir düsünün? Yada ben
söyliyeyim. Muhtemelen sonsuz bir döngü içerisinde sistem saatini kontrol edip belirli bir
aralık geçmis ise kaynağı kontrol eden islevi çağıracaktınız. Eğer zamansal bir islem
yapıyorsak ki bir olayı bir zaman dilimi içinde sürekli gerçeklestirmek zamana bağlı bir
olaydır; mutlaka sistem saatinden faydalanmamız gerekir. Sistem saatini makine
düzeyinde isleyen bir birim olarak düsünebilirsiniz. Bilgisayar bilimlerinde bir çok
algoritma sistem saati üzerine kurulmustur. Zatan eğer zamanı ölçebilecek bir birim
olmasaydı su anki teknolojik imkanlara kavusmamız mümkün olamazdı. Sonuç olarak
zaman ne kadar değerliyse zamanı ölçebilmek te o derece önemlidir.
Bu yazıda bir metodu bir zaman dilimi içerisinde tekrar tekrar çağırmayı öğreneceğiz.
Bunun için System.Timers isim alanında ki Timer isimli sınıfı kullanacağız. Timer sınıfında
bulunan Elapsed olayı timer nesnesinin Interval özelliği ile belirlenen aralıkta
çalıstırılacak metodun referansını tutmaktadır. Metodun referansı Timer sınıfının içindeki
delegate(temsilci) ve olay(event) veri yapıları ile sağlanmaktadır. (Delegate ve Event veri
yapıları ile ilgili ayrıntılı yazılar ileriki zamanlarda yayınlanacaktır.)
Simdi isterseniz Timer sınıfının yapıcı metotlarını inceleyelim.Timer sınıfının iki tane yapıcı
metodu vardır. Bu metotlardan birincisi herhangi bir parametre almamaktadır. Đkincisi ise
double türden bir parametre almaktadır. Double türden olan bu parametre Timer
nesnesinin Elapsed olayının ne kadar sürede meydana geleceğini belirtmektedir. Bu
metodun prototipi asağıdaki gibidir.
public Timer(double TickSayısı)
TickSayisi milisaniye cinsindendir. Örneğin her 1 saniyede çalısmasını istediğimiz bir
metot için TickSayisi 1000 olan bir Timer kullanmamız gerekir. Saniyede on defa
çalısmasını istediğimiz bir metot için ise TickSayisi 100 olan bir Timer nesnesi
kullanmalıyız. Eğer yapıcı metot herhangi bir parametre ile kullanılmaz ise varsayılan
TickSayisi olan 100 ile Timer nesnesi olusturulur.
Çalısma zamanında timer nesnelerinin TickSayisini değistirmek yada Tick sayısını
öğrenmek için Interval özelliği kullanılabilir. Timer nesneleri varsayılan olarak aktif
değillerdir. Bu yüzden Timer nesnelerinin isleyebilmesi için bool türden olan Enabled
özelliğinin true olarak ayarlanması gerekir. Yada Timer sınıfının Start() metodunu
kullanarak zamanlayıcıyı baslatmanız gerekmektedir.
Timer sınıfı ile ilgili en önemli nokta Interval ile belirtilen süre dolduğunda hangi metodu
çağıracağını nerden anlayacağıdır. Bunun için temsilciler ve olaylar kullanılır. Timer sınıfı
tasarlanırken yapısında tanımlanan event ve delegate veri türleri ile bu mümkün
olmaktadır. Timer sınıfının Elapsed olayı bu ise yaramaktadır. Elapsed olayına +=
operatörü ile olay yöneticisi yardımıyla(event handler) yeni metotlar ekleyebiliriz. Timer
sınıfının Elapsed olayı için ElapsedEventHandler isminde özel bir temsilci tanımlanmıstır.
Bu temsilci nesnesine parametre olarak verilecek metodun parametreik yapısı asağıdaki
gibi olmalıdır.
SaniyelikIs(object o, ElapsedEventArgs a)
ElapsedEventArgs sınıfı Timer nesnesi ile ilgili ayrıntılı bilgiler tutmaktadır. Örneğin
ElapsedEventArgs sınıfının SignalTime özelliği ile Elapsed olayının meydana geldiği saati
ve tarihi öğrenebiliriz. Örneğimizi verdikten sonra ElapsedEventArgs sınıfını daha iyi
öğreneceksiniz.
Timer sınıfının diğer bir önemli elemanı ise Stop() metodudur. Bu metodu çağırdığımız
anda Timer nesnesinin Elapsed olayı artık meydana gelmez. Stop() metodu yerine Timer
nesnesnin Enabled özelliğini false yapmak ta diğer bir çözümdür.
Timer sınıfı ile ilgili bu bilgileri verikten sonra bir örnek ile bilgilerimizi pekistirelim.
Örneğimiz de her saniye de saati ekrana yazadıracak bir metot bildireceğiz. Bu metot bir
Timer sayesinde her saniye çağrılacaktır. Örneğin kaynak kodu asağıdaki gibidir.
using System;
using System.Timers;
class Zamanlayici
{
static void Main()
{
Timer t = new Timer(1000);
t.Elapsed += new ElapsedEventHandler(SaniyelikIs);
t.Start();
while(true)
{
}
}
static void SaniyelikIs(object o, ElapsedEventArgs a)
{
Console.WriteLine(DateTime.Now);
}
}
Timer sınıfının islevini görebilmek için programımızın en az bes on saniye çalısmasına
devam etmesi gerekir. Bunun için while(true) seklinde sonsuz bir döngü kurduk. Eğer bu
sonsuz döngü olmasaydı programın icrası bir saniyeden kısa bir sürede biteceği için
Elapsed olayı meydana gelmeyecekti. Programın çalısması sırasında elde edilmis bir ekran
görüntüsü asağıdaki gibidir.
Ekran çıktısından da görüldüğü üzere her saniyede ekrana yeni saat değeri yazıldığı için
programımız bir saat gibi çalısmaktadır.
SaniyelikIs() metodunu ElapsedEventArgs sınıfından dolayı asağıdaki gibi de
yazabilirdik. ElapsedEventArgs sınıfının SignalTime özelliği Elapsed olayının meydana
geldiği zamanı vermektedir.
static void SaniyelikIs(object o, ElapsedEventArgs a)
{
Console.WriteLine(a.SignalTime);
}
Timer sınıfını kullanırken karsılasabileceğimiz en büyük sorunlardan biri Elapsed olayının
meydana gelme aralığının olaydan sonra çalıstırılacak kodların icrası için geçen zamandan
az olmasında görülür. Bu sorun, ikinci Elapsed olayı meydana geldiğinde birinci Elapsed
olayına iliskin kodlar halen çalısıyor durumda olmasından kaynaklanır. Örneğimizdeki
Timer nesnesinin Tick sayısını 1000 yerine 100 yaptığımızda aynı saat değerinin 10 defa
ekrana yazdırılması gerektiğini anlarız. Çünkü Elapsed olayı her saniyede 10 defa
gerçeklesecektir. Her 10 Elapsed olayı gerçeklestiğine bir saniye geçeceği için ekrana aynı
saat değeri 10 defa yazmalıdır diye düsünürüz. Ama bunun gerçekte böyle olmadığını
görürüz. Tick sayısını 100 yaptığımızda programın ekran çıktısı asağıdaki gibi olmaktadır.
Ekran çıktısında bazı saat değerlerinin daha fazla sayıda bazılarının ise daha az sayıda
yazıldığını görüyorsunuz. Bu demek oluyorki programın bellek durumuna ve islemcinin
yoğunluğuna göre Elapsed olayının meydana gelme sayısı değismektedir. Bunu
önlemenin yolu tick sayısının, Elapsed olayından sonra çalısacak metot yada metotların
toplam icra süresinde daha fazla olacak sekilde ayarlamaktır.
Elapsed olayına birden fazla metotodu iliskilendirebiliriz. Örneğin Elapsed olayı her
saniyede bir meydana geldiğinde ekrana ayrıca "Merhaba Elapsed olayı!" yazdırmak için
asağıdaki programı yazabilirsiniz.
using System;
using System.Timers;
class Zamanlayici
{
static void Main()
{
Timer t = new Timer(1000);
t.Elapsed += new ElapsedEventHandler(SaniyelikIs);
t.Elapsed += new ElapsedEventHandler(Selam);
t.Start();
while(true)
{
}
}
static void SaniyelikIs(object o, ElapsedEventArgs a)
{
Console.WriteLine(DateTime.Now);
}
static void Selam(object o, ElapsedEventArgs a)
{
Console.WriteLine("Merhaba Elapsed olayı!");
}
}
Bu programda Elapsed olayı meydana geldiğinde ilk önce SaniyelikIs() metodu ardından
da Selam() metodu çağrılacaktır. Bunun sebebi olayla iliskilendirilen metotların olay
yöneticisine eklenme sırasıdır. Đlk eklenen metot ilk çağrılır. Bu programın ekran çıktısı
ise asağıdaki gibi olacaktır.
Bu yazının sonuna geldik. Size Timer sınıfının kullanımını ve dikkat etmeniz gereken bazı
noktaları aktarmaya çalıstım. Yazı ile ilgili düsünceleriniz elektronik posta adresime
yazabilirsiniz.
C#'ta Gösterici(Pointer) Kullanmak – II
C#'ta göstericilerin kullanımına yönelik ilk yazıda göstericilere giris yapmıstık, C#'ta
göstericilerin kullanımını 3 yazılık bir seri halinde anlatmayı düsündüm. Bu yazıda
gösterici aritmetiğini ve fixed anahtar sözcüğünün kullanımını öğreneceğiz.
Göstericilerin adres bilesenlerine sabit tamsayi degerleri ekleyebiliriz, ayni sekilde
göstericilerin adres bileseninden sabit bir tamsayi degerini çikarabiliriz. Ancak
göstericilere uygulanan bu toplama ve çikarma islemleri biraz farklidir. Göstericilere sabit
degerlerin eklenmesi yada bir degerin göstericiden çikarilmasi göstericideki tür bileseni ile
yakindan ilgilidir. Bir göstericinin degerini bir artirmak göstericinin adres bilesenini,
göstericinin türünün içerdigi byte sayisi kadar artirmak demektir. Ayni kural çikarma
islemi içinde geçerlidir. Örnegin int türünden bir gösterici ile 1 sayisini toplamak
göstericinin adres bilesenini 4 artirmak anlamina gelir. Çünkü int türü 4 byte
büyüklügündedir. Ayni sekilde int türden bir göstericiden 1 sayisini çikarmak göstericinin
adres bilesenini 4 eksiltmek anlamina gelir. Göstericilerle yapilan bu tür aritmetik
islemlerin tamamina gösterici aritmetigi denilmektedir.
Gösterici aritmetigini daha yakindan görmek için asagidaki programi yazin ve sonucunu
inceleyin.
using System;
class Gosterici
{
unsafe static void Main()
{
int* ptr1 = (int*)500;
char* ptr2 = (char*)500;
double* ptr3 = (double*)500;
byte* ptr4 = (byte*)500;
ptr1 += 2;
ptr2 += 5;
ptr3 += 2;
ptr4 += 6;
Console.WriteLine((uint)ptr1);
Console.WriteLine((uint)ptr2);
Console.WriteLine((uint)ptr3);
Console.WriteLine((uint)ptr4);
}
}
Programi /unsafe argümani ile derleyip çalistirdigimizda asagidaki ekran görüntüsünüz
elde ederiz. Programda Main() metodunun unsafe olarak isaretlendigine dikkat edin.
508
510
516
506
Programin çiktisindan da görüldügü üzere int türden bir göstericiye 2 sayisini eklemek
göstericinin adres bilesenini 2*4=8 kadar artirmistir. Ayni sekilde char türden bir
göstericiye 5 degerini eklemek göstericinin adres bilesenini 5*2 =10 kadar artirmistir.
Toplama yerine çikarma islemi yapilmis olsaydi bu sefer ayni oranda adres bileseni
eksiltimis olacakti.
Göstericiler üzerinde sadece tamsayilarla aritmetik islemler yapilabilir. Göstericiler ile
asagidaki aritmetik operatörleri kullanabiliriz.
+ , - , -- , ++ , -=, +=
void göstericilerde herhangi bir tür bilgisi olmadigi için bu tür göstericiler üzerinde
aritmetik islemler yapilamaz. Çünkü void türünden bir göstericiye örnegin 1 eklemek
istedigimizde göstericinin adres bileseninin kaç byte ötelenecegi belli degildir.
Göstericiler üzerinde yapilabilecek diger önemli islemde iki göstericinin birbirinden
çikarilmasidir. Gösterici türleri ayni olmak sartiyla iki göstericiyi birbirinden çikarabiliriz.
Ancak iki göstericinin çikarilmasi sonucunda üretilen deger bir gösterici türü degildir. Iki
gösterici arasindaki fark, adres bilesenlerinin sayisal farkinin gösterici türlerinin
büyüklügünden kaç adet byte miktari edecegidir. Diger bir deyisle adres bilesenlerinin
sayisal farki alinip gösterici türünün byte miktarina göre bir deger belirlenir. Iki
göstericinin farki long türden bir deger üretir. Iki göstericinin farkina örnek verecek
olursak, int türden 5008 adres ile int türden 5000 adresinin farki (5008-5000) %
sizeof(int) tir. Yani sonuç long türden 2 dir. Asagidaki programi yazarak sonucu
görebilirsiniz.
using System;
class Gosterici
{
unsafe static void Main()
{
int* ptr1 = (int*)500;
int* ptr2 = (int*)508;
long fark=ptr2 - ptr1;
Console.WriteLine(fark);
}
}
Diger bir ilginç nokta iki göstericinin adres bilesenlerinin farki gösterici türlerinin
büyüklügünün tam kati olmadiginda görülür. Örnegin ptr2 göstericisini tanimlanmasini
int * ptr2 = (int*)507;
seklinde degistirdiginizde bu sefer ekrana 1 yazdigini görürsünüz. Burdan çikarmamiz
gereken sonuç iki göstericinin farki adres bilesenlerinin sayisal farkinin olmamasidir.
Dikkat: Iki göstericinin farki long türden bir deger üretir. Bu yüzden iki göstericinin farki
açikca bir tür dönüsümü yapilmadikça long türünden küçük türden olan degiskenlere
atanamaz.
Göstericiler ile kullanilabilecek diger operatörler ise ==, < ve > gibi karsilastirma
operatörleridir. Bu operatörler iki göstericinin adres bilesenini karsilastirip ture yada false
degeri üretirler. Karsilastirma operatörleri göstericiler için çok istisnai durumlar disinda
anlamli degildir. Bu istisna durumlardan biri göstericileri kullanarak dizi islemleri
yaptigimizda görülür.
fixed Anahtar Sözcügü
Bildiginiz gibi C#' ta tanimladigimiz referans degiskenleri heap bellek bölgesindeki
adresler temsil ederler. Ancak biz adresler yerine nesnenin ismini kullaniriz. Gereksiz bilgi
toplayicis(garbage collector) bellek optimizasyonui açisindan heap bellek bölgesindeki
nesnelerin yerlerini her an degistirebilir. Bu yer degisiminden bizim haberimiz olmaz,
çünkü nesnenin yeri degistigi anda bu nesneye referans olan stack bellek bölgesindeki
degiskenin adres bileseni de degistirilir. Dolayisiyla biz ayni referans ile farkli bellek
bölgesini istegimiz disinda kullanmis oluruz. Ancak bazi durumlarda gereksiz nesne
toplayicisina bir nesnenin adresini degistirmemesi için ikna etmek durumunda kaliriz. Bu,
özellikle sinif nesnelerinin üye elemanlarindan birinin adresi ile islem yapmamiz gerektigi
durumlarda karsimiza çikar. Bir degiskenin adresinin belirlenen bir faaliyet alani boyuncu
degismeden kalmasi için bunu gereksiz nesne toplayicisina bildirmemiz gerekir. Bunun
için fixed anahtar sözcügü kullanilir.
Zaten fixed anahtar sözcügünü kullanmadan referans türünden nesnelerin üye
elemanlarinin adreslerini elde etmemiz mümkün degildir. Üye elemanlarinin adreslerini
elde edemedigimiz bu tür nesnelere managed type(yönetilen tip) denilmektedir. Buna
göre siniflar managed type kapsamina girmektedir.
Asagidaki programda ManagedType isimli sinifin int türden olan x elemaninin adresi bir
göstericiye atanmak isteniyor.
using System;
class ManagedType
{
public int x;
public ManagedType(int x)
{
this.x = x;
}
}
class Gosterici
{
unsafe static void Main()
{
ManagedType mt = new ManagedType(5);
int* ptr1 = &(mt.x);
}
}
ManagedType sinifinin x elemani deger tipi olmasina ragmen mt nesnesi üzerinden x
degiskeninin adresi elde edilememektedir. Çünkü x degiskeninin adresi gereksiz nesne
toplayicisi tarafindan her an degistirilebilir. Eger yukaridaki kod geçerli olmus olsaydi x
degiskeninin adresi degistigi anda ptr1 göstericisi nereye ait oldugu bilinmeyen bir adres
bilgisi tasiyor olacakti. x degiskeninin bir blok içerisinde sabit adreste olmasini istiyorsak
asagidaki gibi fixed anahtar sözcügünü kullanmaliyiz.
using System;
class ManagedType
{
public int x;
public ManagedType(int x)
{
this.x = x;
}
}
class Gosterici
{
unsafe static void Main()
{
ManagedType mt = new ManagedType(5);
fixed(int* ptr1 = &(mt.x))
{
//x'in adresi bu blokta asla degismez.
}
}
}
Yukaridaki fixed ile isaretlenmis blokta x'in adresinin degismeyecegi garanti altina
alinmistir. Birden fazla degiskeni fixed olarak isaretlemek için asagidaki gibi bir kullanim
geçerli kilinmistir.
ManagedType mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);
fixed(int* ptr1 = &(mt1.x))
fixed(int* ptr2 = &(mt2.x))
{
//x'in adresi bu blokta asla degismez.
}
Öte yandan bir fixed bildirimi içinde adreslerinin degismesini istemedigimiz elemanlari
virgül ile ayirarak asagidaki gibi de bildirebiliriz.
ManagedType mt1 = new ManagedType(5);
ManagedType mt2 = new ManagedType(5);
fixed(int* ptr1 = &(mt2.x), ptr2 = &(mt2.x))
{
//x'in adresi bu blokta asla degismez.
}
Göstericilerle ilgili son yazıda, yapı göstericileri, göstericiler ile dizi islemleri ve
stackalloc ile dinamik alan tahsisatı yapma gibi konuları inceleyeceğiz.
C# ile Nesne Yönelimli Programlama I
Bu makalede nesne yönelimli programlama tekniğine kadar kullanılan yazılım
gelistirmedeki yaklasımlara göz atacağız. Daha sonra nesne yönelimli programlamanın
temel kavramları ve neden böyle bir tekniğin kullanıldığı üzerinde duracağız.
Yazılım Gelistirme ve Bu Alandaki Yaklasımlar
Kimilerine göre geçtiğimiz yüzyılın en önemli bulusu olarak kabul edilen bilgisayar
teknolojisi, bas döndürücü bir hızla gelismektedir. Bilisim sektöründeki değisimler bazen
varolan teknolojilere yenilerinin eklenmesi seklinde olabilir. Diğer taraftan bir kısım
yenilikler vardır ki bu alanda büyük değisimlere ve evrimlere yolaçar. Mesela; kisisel
bilgisayarların kullanılmaya baslanması veya internetin belli baslı akademik kurumların ve
askeri organizasyonların tekelinden alınıp tüm insanlığın hizmetine sunulması gibi.
Hepimizin bildiği gibi bir bilgisayar sistemi iki ana parçadan olusur. Bunlar
donanım(hardware) ve yazılım(software). Donanımın yazılım ile uyumlu çalisması
sonucunda sistemlerimiz sorunsuz bir sekilde bizlere hizmet verirler. Ayrıca donanımın
amacımimza uygun hizmet vermesi uygun yazılımın gelistirilip kullanılmasına baglıdır.
Yazılım sektöründe program gelistirme konusunda günümüze kadar bir çok yaklasim
denenmistir. Bunların ilki programın bastan asağıya sırası ile yazılıp çalıstırılmasıdır. Bu
yaklasımla BASIC dili kullanılarak bir çok program yazıldığını biliyoruz. Burda sorun
programın akısı sırasında değisik kısımlara goto deyimi ile atlanmasıdır. Program kodu
bir kaç bin satır olunca, kodu okumak ve yönetmek gerçekten çok büyük sorun oluyordu.
ıkinci yaklasım ise prosedürel yaklasımdır. Programlarda bir çok isin tekrar tekrar farklı
değerleri kullanılarak yapıldığı farkedildi. Mesela herhangi bir programda iki tarih arasında
ne kadar gün olduğunu bulmak birçok kez gerek olabilir. Bu durumda baslangıç ve bitis
tarihlerini alıp aradaki gün sayısını veren bir fonksiyon yazılabilir ve bu fonksiyon ihtiyaç
duyulduğu yerde uygun parametrelerle çağrılıp istenen sonuç elde edilebilir. Prosedürel
yaklasım Pascal ve C dillerinde uzun yıllar basari ile kullanılmıstır.
Ama her geçen gün programların daha karmasık bir hal alması, program kodunun
kurumsal uygulama projelerinde onbinlerce satırı bulması ve yazılım gelistirme
maliyetinin çok arttiğını gören bilim adamları, programcılara yeni bir yaklasımın
kullanılabilineceğini öğrettiler. Bu yakasımın ismi Nesne Yönelimli Programlama(Object
Oriented Programlama)dır.
Nesne yönelimli programlama tekniği, diger yaklasımlara nazaran, yazılım gelistiren
insanlara büyük avantajlar sağlamaktadır. Birincisi karmasık yazılım projelerinin
üretilmesini ve bakımını kolaylastırıyor olmasıdır. Diğeri ise program kodunun tekrar
kullanılabilmesine (code-reusability) olanak sağlamasıdır. Bu noktada program kodunun
tekrar kullanılabilmesi profesyonel yazılım sirketlerinin maliyetlerini azaltmıstır. Dolayısi
ile programların lisans ücretleri düsmüs ve sektörün sürekli olarak canlı kalmasına ve
rekabet içinde gelismesine yardımcı olmustur.
Nesne Yönelimli Programlama Nedir?
Nesne yönelimli programlamada esas olan, gerçek hayatta varolan olguların
programlamaya aktarılmasındaki yeni yaklasımdır. Prosedürel programlamada verilerimiz
ve fonksiyonlarımız vardı. Yani veri ve bu veriyi isleyen metodlar etrafinda dönüyordu
hersey.
Aslında nesne yönelimli programlamada da iki önemli birim veri ve veriyi isleyip mantıklı
sonuçlar üreten metodlar bulunur. Ama burdaki fark gerçek hayattaki olguların da daha
iyi gözlenip programlama dünyasına aktarılmasındadır.
Mesela elimizde bir ütümüz olsun. Ütünün markası, modeli, rengi, çalıstığı elektrik voltajı,
ne tür kumasları ütüleyebildiği bu ütüye ait özelliklerdir (veri). Aynı zamanda ütümüzü
ısıtabiliriz, ütüleme isinde kullanabiliriz ve soğumaya bırakabiliriz. Bunlar ise ütünün
fonksiyonalarıdır(metod). Eğer ütü ile ilgili bir program yapmıs olsak ve nesne yönelimli
programlama tekniğini kullansak hemen bir ütü sınıfı(class) olustururduk. Bu sınıfta ütüye
ait bilgiler (veriler) ve ütü ile yapabileceğimiz isler(metod) bulunurdu. O zaman nesne
yönelimli programlama da bir sınıfta, sınıfa ait veriler ve bu verileri isleyip bir takiı faydalı
sonuçlar üreten fonksiyonlar/metodlar bulunur.
Dahası, biz birtane ütü sinifi tasarlarsak bu sınıftan istediğimiz sayıda değisik
ütüler(object veya instance) yapabiliriz. Ağagidaki sekilde ütü sınıfı ve bu sınıftan
olusturduğumuz neslerin görsel olarak anlatımı bulunmaktadır.
Sınıf Nedir ve Nasıl Tasarlanır?
Az önce de değindiğimiz gibi, sınıf bir yazılım kurgusudur ve gerçek hayattaki herhangi
bir olguyu modelleyen ve bu olguya ait özellikleri(veri) ve davranısları(metdodlar)
tarifleyen yapıdır.
ısterseniz ütü ile ilgili bir örnek üzerinde çalısalım. Asağıdaki örnek C# program kodunu
lütfen dikkatlice inceleyeniz.
using System;
class Ütü_Örnegi
{
static void Main(string[] args)
{
Ütü ütü1= new Ütü("Beyaz", "AyBakır");
Console.WriteLine(ütü1.Isın(70));
Console.ReadLine();
}
}
class Ütü
{
public int sıcaklık;
public string renk;
public string marka;
public Ütü(string renk, string marka)
{
sıcaklık=15;
this.renk=renk;
this.marka= marka;
Console.WriteLine(sıcaklık+ "derece sıcaklığında,\n "
+ renk + " renginde ve\n "
+ marka +" markasıyla bir ütü nesnesi olusturuldu\n\n");
}
public string Isın(int derece)
{
sıcaklık+=derece;
return "su an sıcaklıgım: " + sıcaklık+ " derece";
}
}
Yukarıdaki örnek programımızda önce altta bulunan Ütü sınıfımızı inceleyelim. Bu sınıfın
sıcaklık, renk ve marka olmak üzere üç adet verisi vardır. Ayrıca Ütü sınıfımızın Ütü(string
renk, string marka) seklinde tanımlanmıs yapılandırıcısı (constructor) vardır.
Yapılandırıcılar bir sınıftan olusturulan nesnelerin ilk değerlerini atama ve baslangıç
islemlerini yapmak için kullanılırlar. Ütü sınıfınımızın yapılandırcısı, olusturulan her ütü
nesnesinin sıcaklığını varsayılan değer olarak 15 dereceye ayarlıyor. Ayrıca paratmetre
olarak alınan renk ve marka değerlerini de atayıp, ütüye ait özellikleri ekrana yazdırıyor.
Ütü sınıfına ait olan diğer metod ise Isın(int derece) olarak tanımladığımız metoddur. Bu
metod ütünün sıcaklığını derece parametresinde verilen değer kadar artırıp sonucu string
tipinde geri dönderiyor.
Ütü_Örnegi sınıfına geri dönersek, burda sadece Main() metodunun bulunduğunu
görürüz. Main() metodu, C# dilinde her programda bulunması gereken zorundu bir
metoddur. Çünkü programın çalistırılması buradan baslar. Ayrıca her C# programında
sadece bir tane Main() metodu bulunur. Örneğimizde Main() metodundaki en önemli
satir:
Ütü ütü1= new Ütü("Beyaz", "AyBakır");
satırıdır. Bu satırda Ütü sınıfına ait ütü1 nesnemizi olusturuyoruz. Yukarıdaki satırdan da
görebileceğimiz gibi herhangi bir sınıfa ait yeni nesneyi olustururken genel olarak su yaps
kullanılır:
SınıfAdı nesneAdı = new SınıfAdı(parametre1, parametre2, … parameter N)
Referans Tipleri ve Nesneler
Nesnelerin en önemli özelliği referans tipinde olmalarıdır. Referans tiplerinde bir nesnenin
değeri saklanmaz. Sadece nesnenin hafızadaki (heap memory) yeri saklanır. Bir nesnenin
hafizadaki yerini new operatörü geri dönderir. Asağıdaki program kodu ile referans
tiplerine ait bir kaç temel özelliği inceleyelim:
using System;
class Ütü_Örnegi
{
static void Main(string[] args)
{
Ütü ütü1= new Ütü("Beyaz", "AyBakır");
Console.WriteLine("ütü1'nin ilk özellikleri: " +
ütü1.Özellikler());
Console.WriteLine("ütü1'i 50 derece ısıtalım: "+
ütü1.Isin(50)+ "\n\n");
// ütü2 nesnemizi renksiz ve markasız olarak olusturalım:
Ütü ütü2 = new Ütü("","");
//ütü2 nesnemizin özelliklerine bir göz atalım:
Console.WriteLine("ütü2'nin ilk özellikleri: " +
ütü2.Özellikler());
ütü2= ütü1; // ütü2 nesnemizi ütü1 nesnemize atıyoruz.
Console.WriteLine("ütü2'nin özellikleri: " +
ütü2.Özellikler());
Console.ReadLine();
}
}
class Ütü
{
public int sıcaklık;
public string renk;
public string marka;
public Ütü(string renk, string marka)
{
sıcaklık=15;
this.renk=renk;
this.marka= marka;
}
public string Isın(int derece)
{
sıcaklık+=derece;
return "su an sıcaklığım: " + sıcaklık+ " derece";
}
public string Özellikler()
{
string sonuc= " Sıcaklık: " + sıcaklık+
"\n Renk: " + renk+
"\n Marka: " + marka+ "\n\n";
return sonuc;
}
}
ısterseniz kodumuzu incelemeye yine Ütü sınıfından baslayalım. Bir önceki örneğe göre
sınıfımızda degisikler yaptık. Ütü sınıfının özelliklerini gösteren ayrı bir metod yazdık. Bu
metod Özellikler() isimli olsun. Doğal olarak Ütü sınıfının yapılandırıcısında, sınıf
olusturulduktan hemen sonra nesnenin özelliklerini gösteren kısım kaldırıldı.
simdi ise Ütü_Örnegi sınıfındaki Main() metodu üzerinde yoğunlasalım. Yine, birinci
örnekte olduğu gibi, ütü1 nesnemizi
Ütü ütü1= new Ütü("Beyaz", "AyBakır");
ile olusturuyoruz. Sonra ütü1 nesnesinin özelliklerini ekrana yazdırıyoruz. Bir sonraki
satırda ise ütü1 nesnemizi 50 derece ısıtıp sonucu ekrana yazıyoruz.
Artık ikinci bir ütü nesnesinin olusturmanin zamanı geldiğini düsünüp
Ütü ütü2 = new Ütü("","");
satırında ütü2 nesnemizi olusturuyoruz. Burada dikkatinizi çektiği gibi ütü2 nesnesinin
renk ve marka özelliklerini bos olarak (null) olusturuyoruz. ütü2 nesnesini olusturunca
özelliklerine gözatmak için
Console.WriteLine("ütü2'nin ilk özellikleri: " +
ütü2.Özellikler());
satırlarını yazıyoruz. Bir sonaki satır programımızın en önemli satırıdır. Bu satırda ütü2=
ütü1 ifadesi ile ütü2 nesnesinin referansını ütü1 nesnesinin referansına atıyoruz. Değer
tiplerinde "=" operatörü ile bir değiskenin değeri diğerine kopyalanır. Ama nesneler
referans tipleri oldukları için, "=" operatörü bir nesnenin referansı diğerine atar. Bu
durumda ütü2 nesnesi artik ütü1'in elemanlarının adreslerini içerir. Bu andan sonra
ütü2'nin eskiden gösterdiği sicaklik, renk ve marka verilerine erismemiz mümkün
değildir. Artık onlar hafızada bir yerde ve biz bilmiyoruz. Bu verilerin hafızada kapladıkları
alanı .NET Framework'nun çöp toplayıcısı (garbage collector) uygun gördüğü bir anda
temizleyecektir.
ütü2 nesnesinin son durumda özelliklerine bakacak olursak gerçektende ütü1'i referans
ettiğini görürüz. Yukarıdaki programımızı çalıstırdığımızda asağıdaki sonucu elde ederiz:
Son olarak su ilginç soruya cevap vermeye çalısalım. "Bir önceki program kodunun
sonuna asağıdaki kısmı eklersek ne gibi bir değisiklik olur?"
// ütü2 nesnemizin özelliklerini degistiriyoruz:
ütü2.sıcaklık=30;
ütü2.renk="Kırmızı";
ütü2.marka="Dolu";
//ütü1 nesnemizin özellikerine bir göz atalım:
Console.WriteLine("ütü1'nin son durumdaki özellikleri: " +
ütü1.Özellikler());
//ütü1 nesnemizin özellikerine bir göz atalım:
Console.WriteLine("ütü2'nin son durumdaki özellikleri: " +
ütü2.Özellikler());
Yukarıdaki kodun eklenmis halini derleyip çalıstirmayı deneyelim. Sonuç asağıdaki gibi
olacaktır:
Evet ütü1 ve ütü2'nin son durumdaki özellikleri aynıdır. Bu durumdan su sonuca
ulasabiliriz: " ütü1 ve ütü2'nin herhangi biri ile özellikleri degistirirsek diğeri de aynı
özelliklere sahip olur. Çünkü her iki nesne de hafızada aynı yere referans ediyorlar."
Bu makalede nesne yönelimli programlamaya giris yaptık. Nesne yönelimli
programlamanın iki temel kavramı olan sınıf(class) ve nesne(object)'yi inceledik. Sonraki
makalelerde nesne yönelimli programlamanın diğer önemli özellikerini birlikte incelemek
dileğiyle.
C#'ta Gösterici(Pointer) Kullanmak – III
C#'ta göstericilerin kullanımı ile ilgili yazı dizisinin son bölümü olan bu yazıda gösericiler
ile dizi islemlerinin nasıl yapıldıgi, stackalloc ıle dinamik bellek tahsısatının yapılmasını ve
son olarak yapı(struct) göstericilerinin kullanılısını inceleyeceğiz.
Göstericiler ile Dizi Đslemleri
C#'ta tanımladıgımız diziler System.Array sınıfi türündendir. Yani bütün diziler managed
type kapsamına girerler. Bu yüzden tanımladıgımız bir dizinin herhangi bir elemanının
adresini fixed bloğu kullanmadan bir göstericiye atayamayız. Bu yüzden göstericiler ile
dizi islemleri yaparken ya fixed bloklari kullanıp gereksiz nesne toplayıcısını uyarmalıyız
yada stackalloc anahtar sözcüğünü kullanarak kendimiz unmanaged type(yönetilemeyen
tip) dizileri olusturmalıyız. Her iki durumuda birazdan inceleyeceğiz.
Bildiğiniz gibi dizi elemanları bellekte ardısıl bulunur. O halde bir dizinin elemanlarını elde
etmek için dizinin ilk elemanının adresini ve dizinin boyutunu bilmemiz yeterlidir.
System.Array sınıfı dizilerinin elemanlarina göstericiler yardimiyla rahatlıkla ulasabiliriz.
Bunun için ilk olarak fixed ile isaretlenmis bir blok içerisinde dizinin ilk elemanının
adresini bir göstericiye atamalıyız. Ardından göstericinin degerini bir döngü içerisinde
birer birer artırdıgımızda her döngüde dizinin bir sonraki elemanina ulasmıs oluruz.
Dizilere bu sekilde erisebilmemizi sağlayan ise gösterici aritmetiğidir. Tabi dizilerin
elemanlarının bellekte ardısıl bulunması da bu islemi bu sekilde yapmamızı sağlayan ilk
etkendir.
Simdi yönetilen türden(managed) bir dizinin elemanlarına nasıl eristiğimizi bir örnek
üzerinde inceleyelim.
using System;
class Gosterici
{
unsafe static void Main()
{
int[] a = {1,2,3,4};
fixed(int* ptr = &a[0])
{
for(int i=0; i<a.Length; ++i)
Console.WriteLine(*(ptr+i));
}
}
}
Programı derlediğinizde dizinin elemanlarının
1
2
3
4
seklinde ekrana yazdırıldığını görürsünüz. (programı derlerken /unsafe argümanını
kullanmayı unutmayın). Programın kritik noktası
fixed(int* ptr = &a[0])
satırı ile dizinin ilk elemanının adresinin elde edilmesidir. Zira dizinin diğer elemanlarıda
sırayla ilk elemandan itibaren her eleman için adres değeri 4 byte artacak seklindedir.
Diger önemli nokta ise for döngüsü içindeki
*(ptr+i)
ifadesidir. Bu ifade her döngüde ptr göstericisinin adres bileseni, döngü degiskeni kadar
artırılıyor, sözgelimi döngü degiskeni 1 ise ptr'nin adres bileseni 4 artırılıyor. Bu da dizinin
ikinci elemanının bellekte bulunduğu adrestir. Đçerik operatörü ile bu adrese erisildiğinde
ise dizinin elemanı elde edilmis olur.
Gösterici dizileri ile ilgili diğer önemli nokta göstericilerin indeksleyici gibi
kullanılabilmesidir. Örneğin yukarıdaki örnekte bulunan
*(ptr+i)
ifadesini
ptr[i]
seklinde degistirebiliriz. Burdan asağıdaki esitlikleri çıkarabiliriz.
*(ptr+0) == ptr[0]
*(ptr+1) == ptr[1]
*(ptr+2) == ptr[2]
*(ptr+3) == ptr[3]
Dikkat: ptr[i] bir nesne belirtirken (ptr+i) bir adres belirtir.
Dizilerin isimleri aslinda dizilerin ilk elemanının adresini temsil etmektedir. Örneğin
asagıdaki programda bir dizinin ilk elemanının adresi ile dizinin ismi int türden bir
göstericiye atanıyor. Bu iki göstericinin adres bilesenleri yazdırıldıgında sonucun aynı
olduğu görülmektedir.
using System;
class Gosterici
{
unsafe static void Main()
{
int[] a = {1,2,3,4};
fixed(int* ptr1 = a, ptr2 = &a[0])
{
Console.WriteLine((uint)ptr1);
Console.WriteLine((uint)ptr2);
}
}
}
Programı derleyip çalıstırdığınızda ekrana alt alta iki tane aynı sayının yazıldığını
görürsünüz.
Yönetilen(managed) tiplerle çalısmak her ne kadar kolay olsa da bazı performans
eksiklikleri vardır. Örneğin bir System.Array dizisinin bir elemanına erismek ile stack
bölgesinde olusturacagımız bir dizinin elemanına ulasmamız arasında zaman açısından
büyük bir fark vardır. Bu yüzden yüksek performanslı dizilerle çalısmak için System.Array
sınıfının dısında stack tabanlı diziler olusturmamız gerekir. Stack tabanlı diziler
yönetilemeyen dizilerdir. Bu yüzden bu tür dizileri kullanırken dikkatli olmalıyız. Çünkü
her an bize tahsis edilmeyen bir bellek alanı üzerinde islem yapiyor olabiliriz. Ancak
yönetilen dizilerde dizinin sınırlarını asmak mümkün degildir. Hatırlarsanız bir dizinin
sınırları asılınca çalısma zamanında IndexOutOfRangeException istisnai durumu
meydana geliyordu. Oysa stack tabanlı dizilerde dizinin sınırları belirli degildir ve tabiki
dizinin sınırlarını asmak kısıtlanmamıstır. Eğer dizinin sınırları asılmıssa muhtemelen bu
islem bir hata sonucu yapılmıstır. Hiçbir programcı kendisine ait olmayan bir bellek
alanında islem yapmamalıdır. Aksi halde sonuçlarına katlanması gerekir.
Stack tabanlı diziler stackalloc anahtar sözcüğü ile yapılır. stackalloc bize istediğimiz
miktarda stack bellek bölgesinden alan tahsis eder. Ve tahsis edilen bu alanın baslangıç
adresini geri döndürür. Dolayısıyla elimizde olan bu baslangıç adresi ile stackalloc ile bize
ayrılmıs olan bütün bellek bölgelerine erisebiliriz. stackalloc anahtar sözcüğünün kullanımı
asağıdaki gibidir.
int * dizi = stackalloc int[10];
Bu deyim ile stack bellek bölgesinde 10*sizeof(int) = 40 byte'lık bir alan programcının
kullanmasi için tahsis edilir. Bu alan, dizinin faaliyet alanı bitinceye kadar bizim
emrimizdedir. Tahsis edilen bu 40 byte büyüklüğündeki bellek alanının ilk byte'ının adresi
ise int türden gösterici olan dizi elemanına aktarılır. Dolayısyla dizi göstericisi ile içerik
operatörünü kullandığımızda bize ayrılan 10 int'lik alanın ilk elemanına erismis oluruz.
! stackalloc ile tahsis edilen bellek alanlarının ardısıl olması garanti altına
alınmıstır.
stackalloc ile alan tahsisatı yapılır. Ancak alan tahsisatı yapılan bellek bölgesi ile ilgili
hiçbir islem yapılmaz. Yani yukaridaki deyim ile, içinde tamamen rastgele değerlerin
bulundugu 40 byte'lık bir alanımız olur. Bu alandaki değerlerin rastgele degerler olduğunu
görmek için asagidaki programı yazın.
using System;
class Gosterici
{
unsafe static void Main()
{
int * dizi = stackalloc int[10];
for(int i=0; i<10;++i)
Console.WriteLine("*(dizi+{0}) = {1}",i,dizi[i]); }
}
}
! Yukarıdaki programda bir gösterici ile bellekteki ardısıl bölgelere indeksleyici
operatörü ile nasıl eristiğimize dikkat edin.
Programı /unsafe argümani ile beraber derleyip çalıstırdıktan sonra asağıdaki ekran
görüntüsünü elde etmeniz gerekir. Tabi bu değerler rastgele oldugu için sizdeki görüntü
tamamen farklı olacaktır. Rastgele değerden kasıt çalısma zamanında Random gibi bir
sınıfin kullanılıp rastgele bir sayı üretilmesi değildir. Burdaki sayılar daha önce çalısmis
olan programlardan kalan çöp değerlerdir.
C:\Programlar\StackAlloc
*(dizi + 0) = 0
*(dizi + 1) = 1244236
*(dizi + 2) = 1243328
*(dizi + 3) = 1350496
*(dizi + 4) = 124334
*(dizi + 5) = 1287174
*(dizi + 6) = 1243328
*(dizi + 7) = 0
*(dizi + 8) = 0
*(dizi + 9) = 1243404
C:\Programlar\StackAlloc
Console.WriteLine("*(dizi+{0}) = {1}",i,dizi[i]);
satırındaki
dizi[i]
yerine
*(dizi + i)
yazmamız herhangi birseyi değistirmezdi. Daha önce bu iki kullanımın esdeğer olduğunu
belirtmistik.
Stack tabanli dizilerle ilgili bilinmesi gereken en önemli nokta dizinin sınırlarının asılması
ile ilgilidir. Daha önceden de denildiği gibi stack tabanli dizilerin sınırları asıldığında
herhangi bir uyarı verilmez. Elbetteki program basarıyla derlenir ancak çalımma
zamanında bize ait olmayan bir adresin içeriğini degistirmis oluruz ki bu da bir
programcının basina gelebilecek en tehlikeli durumdur. Örneğin asağidaki programda
stackalloc ile 10 int türünden nesnelik alan tahsis edilmektedir. Buna rağmen istediğimiz
kadar alanı kullanabiliyoruz.
using System;
class Gosterici
{
unsafe static void Main()
{
int * dizi = stackalloc int[10];
for(int i=0; i<50;++i)
*(dizi+i) = i;
}
}
stackalloc ile olusturacağımız dizilerin boyutu derleme zamanında bilinmek zorunda
değildir. Örneğin çalısma zamanında kullanıcının belirlediği sayıda elemana sahip olan bir
ardısıl bellek bölgesi asagıdaki programda oldugu gibi tahsis edilebilir.
using System;
class Gosterici
{
unsafe static void Main()
{
Console.Write("Dizi boyutu gir: ");
uint boyut=0;
try
{
boyut = Convert.ToUInt32(Console.ReadLine());
}
catch(FormatException e)
{
Console.WriteLine(e.Message);
}
int * dizi = stackalloc int[(int)10];
for(int i=0; i<(int)boyut; ++i)
{
*(dizi+i) = i;
Console.WriteLine(dizi[i]);
}
}
}
Bu program ile çalısma zamanında 7 elemanlı stack tabanlı bir dizinin olusturulduğunu
asağıdaki ekran görüntüsünden görebilirsiniz.
Yapı(Struct) Türünden Göstericiler
int,char,double gibi veri türleri aslında birer yapıdır. Bu konun basında bütün değer tipleri
ile gösterici tanımlayabileceğimizi söylemistik. C# temel veri türlerinin yanısıra kendi
bildirdiğimiz yapılar da değer türündendir. O halde bir yapı göstericisi tanımlayabilmemiz
doğal bir durumdur. Yapı göstericilerinin tanımlanması temel veri türlerinden gösterici
tanımlama ile aynıdır. Ordaki kuralların tamamı yapılar içinde geçerlidir. Fakat yapı
göstericisi tanımlamanın bir sartı vardır, oda yapının üye elemanlarının tamamının değer
tipi olma zorunluluğudur. Örneğin asağıdaki yapı göstericisi tanımlaması geçersizdir.
using System;
struct Yapi
{
int x;
char c;
string s;
public Yapi(int x,char c, string str)
{
this.x = x;
this.c = c;
this.s = str;
}
}
class StackAlloc
{
unsafe static void Main()
{
Yapi yapi = new Yapi(2,'a',"Deneme");
Yapi* pYapi = &yapi;
}
}
Yukarıda 'Yapi' türünden göstericinin tanımlanamamasının sebebi yapının yönetilen
türden(managed type) bir üye elemanının bulunmasıdır. Bu üye elemanı da doğal olaral
string türüdür. Yapı bildiriminden string türünü çıkarıp asağıdaki gibi ilgili değisiklikleri
yaptığımızda 'Yapi' türünden göstericileri tanımlayabiliriz.
using System;
struct Yapi
{
int x;
char c;
public Yapi(int x,char c)
{
this.x = x;
this.c = c;
}
}
class StackAlloc
{
unsafe static void Main()
{
Yapi yapi = new Yapi(2,'a');
Yapi* pYapi = &yapi;
}
}
Yapı göstericileri üzerinden yapı göstericisinin adresine iliskin nesnelerin elemanlarına
özel bir operatör olan -> operatörü ile erisebiliriz. Örneğin yukarıdaki programın Main()
metodunu asağıdaki gibi değistirdiğinizde ekrana yapı nesnesinin x ve c elemanları
yazdırılacaktır. Tabi Yapi'nın üye alamanlarını public olarak değistirmeniz
gerekecektir. Çünkü ok operatörü ilede olsa ancak public olan elemanlara
ulasabiliriz.
unsafe static void Main()
{
Yapi yapi = new Yapi(2,'a');
Yapi* pYapi = &yapi;
Console.WriteLine("yapi.x= " + pYapi->x);
Console.WriteLine("yapi.c= " + pYapi->c);
}
Not: ' -> ' operatörüne ok operatörü de denilmektedir.
Yapının public olan elemanlarına ok operatörü yerine yapı nesnesinin içeriğini * operatörü
ile elde edip nokta operatörü ile de ulasabiliriz. Buna göre yukarıdaki Main() metodu ile
asagidaki Main() metodu esdeğerdir.
unsafe static void Main()
{
Yapi yapi = new Yapi(2,'a');
Yapi* pYapi = &yapi;
Console.WriteLine("yapi.x= " + (*pYapi).x);
Console.WriteLine("yapi.c= " + (*pYapi).c);
}
Her iki Main() metodunun ürettiği çıktı aynıdır.
Göstericilerin en çok kullanıldığı diğer bir uygulama alanı da karakter islemleridir. Bir
yazıyı karekter dizisi olarak temsil edip yazılar ile ilgili islemler yapılabilir. C#' taki string
türünün altında geçeklesen olaylarda zaten bundan ibarettir. Karakter dizileri ile ilgili en
önemli nokta bir yazıyı char türden bir göstericiye atayabilmemizdir. Karekter dizileri olan
stringlerdeki her bir karakter bellekte ardısıl bulunmaktadır. Dolayısıyla yazıdaki ilk
karakterin adresini bildiğimizde yazıdaki bütün karakterlere erisebiliriz. C#'taki string
türü yönetilen tip(managed type) oldugu için char türden bir göstericiye bir yazının ilk
karekterinin adresini atamak için fixed anahtar sözcüğünü kullanmalıyız. Asağıdaki
programda bir yazının char türden göstericiye nasıl atandığını ve bu gösterici ile yazıdaki
her karaktere ne sekilde erisildiğini görüyorsunuz.
using System;
class KarakterDizisi
{
unsafe static void Main()
{
fixed(char* ptr = "Sefer Algan")
{
for(int i=0; ptr[i] != '\0'; ++i)
Console.Write(ptr[i]);
}
}
}
Buradaki en önemli nokta ptr[i]' nin '\0' karakteri ile karsılastırıldığı yerdir. Göstericiler ile
bellekte char türünün büyüklügü kadar ilerlerken yazının nerede sonlandıgını bilemeyiz.
Bunun için
char* ptr = "Sefer Algan"
deyimi ile belleğe yerlestirilen 'n' karakterinden sonra null değerini ifade eden '\0'
karekter yerlestirilir. Bu karektere rastlanıldığı zaman yazının sonuna gelmis
bulunuyoruz.
Not: Göstericilerle ilgili yayınlanan bu makale dizisi yazmıs olduğum ve yakında Pusula
yayıncılıktan çıkacak olan C# kitabından alınmıstır.
.NET'te Dinamik Kontrol Olusturma
Bu yazıda windows ve web uygulamalarında dinamik kontrollerin nasıl olusturulacağını ve
olusturulan dinamik kontrollere erisim yöntemlerini inceleyceğiz. Ayrıca bu yazıda
dinamik kontrol olusturma ile ilgili kapsamlı bir örnek kod yeralmaktadır.
Bir çok uygulamada(özellikle oyunlarda) kontrollerimizin sayısını tasarım asamasında
bilemeyebiliriz. Örneğin hepimizin bildiği mayın tarlası oyununda, oyun alanı bir çok
kareden olusmaktadır. Üstelik oyun alanı, kullanıcının isteğine göre değisebilmektedir.
Eğer tasarım asamasında bütün kontroller form üzerine yerlestirilsmis olsaydı hem bu
kontrollerin yönetimi zorlasacaktı hemde gereksiz yere bir çok kod yazılmıs olacaktı. Buna
benzer bir durum web formları ile programlama yaparken de görülebilir. Örneğin bir
anket sisteminde anketin seçenekleri kadar CheckBox kontrolünü dinamik olarak web
formunun üzerine yerlestirebilmemiz gerekir. Eğer bu imkanımız olmasaydı Web formları
çok esnek bir programlama modeli sunmazdı. Neyse ki Microsoft tasarımcıları herseyi
düsünmüs... :)
Dinamik kontrol olusturmaya geçmeden önce nasıl bir form tasarımı yapmaya
çalıstığımıza bakalım. Asağıdaki formdan da gördüğünüz üzere 8X8'lik bir dama tahtasının
her bir karesi aslında bir Button kontrolünden ibarettir. Tasarım asamasında 64 tane
Button kontrolünü form üzerine yerlestirip herbirni tek tek düzenlemek yerine bir döngü
vasıtası ile istediğimiz sayıda Button kontrolünü olusturup Form elemanına ekleyeceğiz.
ToolBox penceresinde görüdüğünüz bütün kontrolleri temsil eden sınıflar
System.Windows.Forms isim alanında bulunan Control isimli sınıftan türetilmistir. Bazı
kontroller ise yapısında değisik kontrolleri barındıracak sekilde tasarlanmıstır. Örneğin
Form penceresi üzerene eklenen kontroller bu duruma bir örnektir. Kontrolleri gruplamak
için kullanılan GrouopBox ta bu sekildedir. Bu özel kontrollere yeni bir kontrol eklemek
için bu Control türünden olan Controls isimli koleksiyon kullanılmaktadır. Örneğin bir
form üzerine yeni bir Button kontrolü eklemek için asağıdaki deyimleri yazmalıyız.
Button tb = new Button();
Form1.Controls.Add(button)
Yukarıdaki deyimleri bir döngü içinde yapıp olusturduğumuz Button kontrollerinin ilgili
özelliklerini değistirdikten sonra dama tahtasını rahatlıkla olusturabiliriz.
Simdi bu yazının en altında bulunan linki kullanarak projeyi bilgisayarınıza yükleyerek
projeyi açın. Form1 sınıfının içinde bulunan asağıdaki TahatCiz() isimli metodu
inceleyerek kontrollerin dinamik olarak nasıl yaratıldıklarını inceleyin.
private void TahtaCiz(int Boyut1,int Boyut2, int En, int Boy)
{
int satir =0;
int sutun =0;
Button tb = new Button();
for(int i=0; i < Boyut1*Boyut2; i++)
{
if(i % Boyut2 == 0)
{
satir++;
sutun = 0;
}
tb = new Button();
tb.Name = "tb" + i.ToString();
tb.TabIndex = i;
tb.Text = i.ToString();
tb.Size = new System.Drawing.Size(En,Boy);
Point p = new System.Drawing.Point(tb.Size.Width + (sutun-
1)*tb.Width,tb.Size.Height + (satir-2)*tb.Height);
tb.Location = p;
tb.FlatStyle = FlatStyle.Standard;
tb.Click += new System.EventHandler(this.button1_Click);
Butonlar.Add(tb);
this.Controls.Add(tb);
sutun++;
}
this.ClientSize = new Size(tb.Width*Boyut2 ,tb.Height*Boyut1);
satir = 0;
sutun = 0;
Butonlar.TrimToSize();
RenkAyarla();
}
TahtaCiz() isimli metodu biraz inceleyelim. Bu metot kendisine gönderilen bilgiler ısığında
form üzerine dinamik bir dama tahtası çizmektedir. Ayrıca ise yarar bir kaç islem daha
yapmaktadır. Bu metoda gönderilen Boyut1 ve Boyut2 değiskenleri dama tahtasının
boyutlarını belirtmektedir. En ve Boy parametreleri ise tahtadaki her bir karenin enini ve
boyunu belirtmektedir. Bu bilgileri parametre olarak almamız istediğimiz boyutta dama
tahtasını çizebileceğimiz anlamına gelmektedir. Zaten form üzerine konulan bir menü
yardımıyla dama tahtasının boyutu ve karelerin en ve boyu istenildiği gibi
değistirilmektedir. Bu metot sadece butonların form üzerine yerlestirilmesinden
sorumludur. RenkAyarla() isimli diğer bir metot ise yerlestirilen bu butonların bir
algoritmaya göre renklerini ayarlamayı sağlamaktadır. Bu metot içinde olusturulan buton
kontrollerine baska metotlar içinde erisebilmek için olusturulan her kontrol global
düzeyde tanımlanan Butonlar isimli bir Arraylist koleksiyonuna eklenmektedir. Bu
metottaki diğer önemli bir satır ise olusturulan dama tahtasınının boyutlarına göre form
nesnesinin yeniden sekillendirilmesidir. Bu islem
this.ClientSize = new Size(tb.Width*Boyut2 ,tb.Height*Boyut1);
satırıyla yapılmaktadır.
Not: Her bir Button kontrolünün yanyana ve alt alta nasıl yerlestirildiğini daha iyi
anlamak için size tavsiyem kağıt üzerine bir dama tahtası çizip her bir karenin konumuna
iliskin matematiksel bir ifade bulun. Böylece hersey daha açık olacaktır.
Asağıdaki satırda her bir buton kontrolünün Text özelliği döngü değiskeni olan i'ye
atanmaktadır.
tb.Text = i.ToString();
Dinamik kontrolleri olusturma ile ilgili diğer bir önemli nokta da kontrollere iliskin
olayların nasıl çağrılacağıdır. Dikkat ederseniz yukarıdaki metotta olusturulan bütün
kontrollerin Click olayına EventHandler temsilcisi yardımıyla button1_Click() metodu
ilistirilmistir. Olusturulan butonlardan herhangi birine tıkladığınızda bu metot isletilecektir.
Peki hani butonun tıklandığını nasıl anlayacağız. Bunun için button1_Click() metodunun
bildirimine bakalım.
private void button1_Click(object sender, System.EventArgs e)
{
Button b = (Button)sender;
MessageBox.Show(b.Text);
}
Yukarıdaki metodun bir parametresi olan sender değiskeni bu metodun hangi button'un
tıklanması sonucu isletildiğini tutmaktadır. Bize bu imkanı sağlayan elbetteki
EventHandler temsilcisidir(delegate).
Dönüsüm operatörü kullanılarak sender nesnesi Button nesnesine dönüstürülümektedir.
(Bu isleme unboxing denildiğini de hatırlatmak isterim)
Bu durumda, örneğin üzerinde 39 yazan Button'a tıkladığınızda gösterilen mesaj
kutusuna gibi 39 yazacaktır.
Asağıdaki ekran görüntüsündeki dama tahtasının biçimi uygulamadaki Ayarlar
menüsündeki Renk sekmesi tıklanarak olusturulabilir.
Not: Dama tahtası biçiminde bir form olusması için Renk Seçimi penceresindeki Gradyan
Katsayısı ortalanmalıdır. Renk ayarlama isleminin nasıl yapıldığı bu yazı kapsamında
olmadığı için detaylarını anlatmayacağım. RenkAyarla() isimli metodu incelemenizi
tavsiye ediyorum.
Web formları ile dinamik kontrol olusturmak yukarıda anlattıklarımdan farklı değil. Yeni
bir ASP.NET uygulaması açarak asağıdaki gibi bir form tasarlayın.
Dinamik olarak olusturacağımız kontrolleri Page sınıfına akleyebileceğimiz gibi
PlaceHolder kontrolünü de kullanabiliriz. Ben bu is için PlaceHolder kontrolünü tercih
ettim.
"Olustur" isimli butonun Click olayına iliskin metodunu asağıdaki gibi değistirip sonucu
inceleyelim.
private void Button1_Click(object sender, System.EventArgs e)
{
int Adet = Convert.ToInt32(TextBox1.Text);
for(int i=0; i<Adet; ++i)
{
TextBox yeni = new TextBox();
yeni.ID = "Text" + i.ToString();
yeni.Text = "TextBox" + i.ToString();
PlaceHolder1.Controls.Add(yeni);
PlaceHolder1.Controls.Add(new LiteralControl("<br/>"));
}
}
Projeyi derleyip çalıstırın ve TextBox kutusuna bir tamsayı girip "Olustur" butonuna
tıklayın. Đslermleri basarı ile yaptıysanız asağıdaki gibi "PlaceHolder" kontrolü içinde 4
adet TextBox kontrolü dinamik olarak yerlestirilecektir.
Not : Sunucu kontrolü olmayan <br> etiketinin LiteralControl olarak tanımlandığına
dikkat edin.
Dinamik kontrolleri olusturmayı öğrendiğinize göre artık web tabanlı bir mayın tarlası
oyunu yapmanın zamanı geldi sanırım :) Biraz çaba ile bu oyunu rahatlıkla
yapabileceğinizi düsünüyorum.
Visual C#.NET 2003'teki Yenilikler
Bu yazıda Visual StudioNET 2003 ile birlilkte gelen Visual C# dilinde yapılan önemli
değisikliklere ve Visual Stdio.NET gelistirme ortamının(IDE) yeni özelliklerine
değinilecektir. Bu yeni özellikler ve değisiklikler 3 ana baslık altında incelenecektir :
Derleyici ve dildeki değisiklikler, proje gelistirme ile ilgili değisiklikler ve VS.NET 2003
IDE'sindeki değisikler.
Giris
Bildiğiniz gibi 2001 yılından beri C# dili ECMA(European Computer Manufacturer's
Assocation) tarafından standart hale getirilmistir. Bu herhangi bir kurum veya kurulusun
istediği takdirde ECMA-334 standartlarına uymak kosulu ile kendi C# derleyicisini
üretebileceği anlamına gelmektedir. Nitekim ECMA-334 standartları çerçevesi içerisinde
su anda farklı platformlar için gelistirilmis C# derleyicileri bulunmaktadır. Bu derleyiciler
içerisinde en çok bilinen ve kullanılanı Microsoft'un Visual C# derleyicisidir. Microsoft,
ECMA standartlarına bağlı kalmak kosulu ile kendi derleyicisini istediği sekilde
biçimlendirebilmektedir. Bu yazıda Visual C# derleyicisine ve dilin yapısına 2003
versiyonu ile birlikte gelen yenilikleri inceleyeceğiz. Ayrıca Microsoft'un Visual Studio.NET
yazılım gelistirme platformu IDE'sine yeni eklediği bir takım özellikleri inceleyeceğiz.
Visual C# Dili ve Derleyicisindeki Değisiklikler
Visual C# 2003 versiyonunda iki ana değisiklik yapılmıstır. Bu değsiklikler eski kodların
çalısmamasına sağlayacak nitelikte değildir. Bu değisikliklerin ilki #line önislemci
komutudur. #line önislemci komutu eski versiyonda zaten bulunmaktaydı, yeni
versiyonda #line önislemci komutuna yeni bir anlam daha yüklenmistir. Yeni kullanımda,
iki #line komutu arasında kalan kod satırı Debugger programı için bilgi sağlamaz. Yani
Debugger için bu kodlar önemsizdir. Program debugger ile çalıstığında ilgili kod satırlara
dikkate alınmaz. Dikkat etmemiz gereken nokta derleme isleminin normal biçimde
yapılmasıdır. Zira #line önislemci komutuna yüklenen bu anlam sadece Debugger(hata
ayıklayıcı) programını ilgilendirmektedir. #line önislemci komutunun bu anlamda
kullanılması için "hidden" parametresinin kullanılması gerekmektedir. Asağıda #line
önislemci komutunun kullanılması ve sonuçlarının görülmesi adım adım anlatılmaktadır.
using System;
class MyClass
{
public static void Main()
{
Console.WriteLine("www"); // Buraya "Breakpoint" koyun.
#line hidden
Console.WriteLine("csharpnedir");
#line default
Console.WriteLine("com");
}
}
Kırmız yazılı satıra bir breakpoint ekledikten sonra Debugger ile birlikte programı
derleyin. Bunun için yapmanız gereken F5 tusuna basmak yada Debug menüsünden Start
sekmesini seçmektir. Program çalıstığında programdaki her 3 satırın da ekrana
yazdırıldığını göreceksiniz, ancak kontrol Debugger programına geçtiğinde Debug
menüsündeki StepOver(F10) komutu ile program içinde satır satır ilerlerken Degugger
programının ikinci satırla ilgilenmediğini göreceksiniz.
Diğer bir değisiklik ise XML yorum satırları ile ilgilidir. Bildiğiniz gibi C# kodu içerisinde
XML formatında yorum satırları eklenebilmektedir. Bu yorum satırları metotlar, sınıflar ve
diğer tipler hakkında dökümantasyon sağlamk için yazılmaktadır. Derleme isleminde
istenirse bu yorum satırlar C# derleyicisi tarafından ayıklanarak rapor halinde sunulabilir.
Eski versiyonda XML yorum satırları 3 tane ters bölü karekterinden sonra yazılırdı.
Örneğin sıklıkla gördüğümüz "summary" elementi asağıdaki gibi yazılabilir
/// <summary>
///
/// </summary>
Yeni versiyonda XML yorum satırları /** ve */ karekterleri arasında da yazılabilir hale
getirilmistir. Bu yeni kurala göre asağıdaki yazım biçimleri ile XML yorum satırları
olusturulabilir.
/**
<summary>yorum</summary>
*/
/** <summary>yorum</summary> */
/**
* <summary>
* yorum</summary>*/
Yukarıdaki her üç kullanımda esdeğerdir.
Bunların dısında bir önceki versiyonla yeni versiyondaki kullanımları birlestirip aynı
kaynak kod içinde asağıdaki gibi de kullanabiliriz.
/**
<summary>Yorum
satiri
*/
/// <summary>
Yapısal Değisiklikler
Visual C# 2003 derleyicisinin ürettiği bazı kodlarda ve içsel mekanizmalarda da değisiklik
olmustur. Bu değisikliklerden en önemlileri özellik(property) bildiriminde ve foreach
döngü yapısının çalısma mantığında görülmektedir.
Sınıfların bir üye elemanı olan özellik bildirimi set ve get anahtar sözcükleri ile yapılır. Bu
yüzden bu özelliklere genellikle "setter" ve "getter" da denilmektedir. Özellik bildirimi C#
derleyicisi tarafından özel metotlara çevrilir. Bu metotlar sırasıyla get_OzellikIsmi() ve
set_OzellikIsmi() metotlarıdır. Dolayısıyla bir nesne üzerinden herhangi bir özelliği
çağırdığımızda aslında bir metot çalıstırmıs oluyoruz. Zaten C++ dilinde bu tür islemleri
yapmak için Get ve Set ile baslayan çesitli metotlar tanımlanırdı. C# bu islemleri biraz
daha soyutlastırarak kullanıcının daha rahat çalısmasını sağlamıstır.
Visual C# 2002'de önemli bir bug vardı. Bu bug özellik bildirimlerinin get ve set
bloklarının varlığından kaynaklanmaktaydı. Örneğin asağıdaki kodda gördüğünüz sınıf
bildirimi herhangi bir hata vermiyordu.
class Deneme
{
public int a
{
get
{
return 5;
}
set
{
value = 5;
}
}
public int get_a()
{
return 3;
}
public int set_a()
{
return 3;
}
}
Bu sınıf bildirimini içeren bir kaynak kodu delendiğinde
Deneme d = new Deneme();
d.a = 5;
satırlarındaki d.a; ile a özelliğinin set bloğunun mu çalıstırılacağı yoksa set_a()
metodunun mu çalıstırılacağı belli değildir. Bu tür bir bildirimin derleme hatasına yol
açması gerekir. Çünkü a özelliğinin bildirimindeki set ve get blokları sayesinde zaten
get_a() ve set_a() metot bildirimleri yapılmıstır. Visual C# 2003 derleyicisi ile bu sınıf
bildirimini içeren bir kaynak kod derlenemeyecektir.
Not: Arayüzlerde bildirimi yapılmıs özellikler için derleme zamanında get_Ozellik() ve
set_Ozellik() gibi iki metot tanımı yapılmaz. Bu yüzden asağıdaki kodda IArayüz
arayüzünden türemis olan sınıf içinde a özelliğinin get ve set blokları tanımlanırken
set_a() ve get_a() metotları kullanılamaz.
interface Deneme : IArayuz
{
public int a
{
get;
set;
}
}
class Deneme : IArayuz
{
//Geçerli Özellik Bildirimi
public int a
{
get
{
return 5;
}
set
{
value = 5;
}
}
//Geçersiz Bildirim
public int IArayuz.get_a()
{
}
//Geçersiz Bildirim
public int IArayuz.set_a(int deger)
{
}
}
Derleyicinin yapısındaki diğer bir değisiklik foreach döngüsünün isletilmesi sırasında
görülmektedir. Bildiğiniz gibi foreach ile iteratif bir sekilde türlere ait dizilerin
elemanlarına erismemiz mümkün, ancak bütün türler için foreach döngüsünü
kullanamayız. Bunun için foreach ile kullanılacak türlerin bazı metotları ve özellikleri
sağlaması gerekir. Bu metotlar ve özellikler IEnumerator arayüzü içinde bildirilmistir.
foreach döngüsünün bitiminde C# derleyicisi döngüde kullanılan türün IDisposable
arayüzünü uygulayıp uygulamadığını kontrol etmiyordu. foreach ile kullanılan tür
IEnumerator arayüzünü uygulasın yada uygulamasın Visual C# 2003 derleyicisi
IDisposable arayüzünün uygulanmıs olup olmadığını kontrol eder ve bu sayede
IDisposable arayündeki Dispose() metodu çağrılır.
Göze çarpan diğer bir değisiklik ise niteliklerin kullanımında görülmektedir. private
elemanı olan nitelikler eski versiyonda kullanılabiliyordu ancak yeni derleyicide sadece
public nitelik elemanları kullanılabilmektedir. Örneğin Deneme isimli nitelik sınıfı ve bu
sınıfın private üye elemanı olan Ozellik bildirilmis olsun. Buna göre asağıdaki metot
bildirimi yeni derleyici için geçersizdir.
[Deneme(Ozellik = "deneme özellik")]
public int Metot()
{
}
Not: Yukarıdaki bildirim Visual C# 2002 derleyicisi için geçerlidir.
Diğer bir değisiklik enum sabitlerinin artık char türüne dönüstürülebilmesidir. Örneğin
asağıdaki programı derleyip çalıstırdığınızda ekrana 'L' karekteri yazdırılacaktır. (L
karekterinin unicode karsılığı 76'dır)
using System;
enum Harfler
{
A,B = 76,C
}
class Class1
{
static void Main()
{
char c = (char)Sefer.B;
Console.WriteLine(c);
}
}
Yukarıda anlatılan değisikliklerin tamamı uygulama performansını artırmak ve ECMA
standartlarına daha sıkı bağlılık amacıyla yapılmıstır. Bir önceki versiyonda ECMA
standartlarına uymayan kurallar ve bazı bug'lar yeni versiyonda düzeltilmistir.
Gelistirme Ortamındaki Değisiklikler
1- Gelistirdiğimiz uygulamaları derlerken çesitli uyarılar yada hatalar alabiliriz. Bu yazılım
gelistirmenin doğal bir sürecidir. Bazı durumlarda derleyicinin verdiği uyarılar gerçekten
can sıkıcı olabilir. Bazende bu uyarılar programcının olası bir hataya karsı önlem alması
için olabilir. VS.NET 2003 ortamında proje derlenirken bazı uyarıların verilmemesini
sağlayabiliriz. Komut satırı derleyicisinde bu islem /nowarn argümanı ile yapılır. Örneğin
derleme islemi sırasında CS0050 ve CS0060 kodlu uyarının verilmemesi için csc
derleyicisi asağıdaki gibi çalıstırılır.
csc dosya.cs /nowarn:50,60
VS.NET kullanarak bu uyarı engelleme isi proje özelliklerinden ayarlanır. Solution Explorer
penceresinde projeye sağ tıklayıp proje özellikleri penceresinden "Configuration
Properties" sekmesini ardından "Build" seçeneğini seçin. Asağıdaki ekran görüntüsündeki
alana uyarı kodlarını noktalı virgül ile birbirinden ayrılacak sekilde yazın.
2- Yeni versiyon ile birliktte istersek /nostdlib argümanını kullanarak mscorlib.dll
kütüphanesini programlarımıza eklemeyebiliriz. Bildiğiniz gibi bütün System isim alanı ve
bu isim alanında bildirilmis türler mscorlib.dll kütüphanesinde bulunmaktadır. Komut
satırından
csc dosya.cs /nostdlibseklindeki
bir derleme ile standart kütüphane olan mscorlib.dll uygulamamıza eklenmez.
Bu islemi kendi System isim alanımızı bildirmek için kullanabiliriz.
/nostdlib+ seklindeki kullanım varsayılan kullanım ile esdeğerdir, yani mscorlib.dll
kütüphanesi uygulamaya eklenir.
VS.NET ortamında bu değisikliği yapmak için proje özelliklerinden "Configuration
Properties" sekmesini ardından "Advanced" seçeneğini seçin. Bu pencereden "Do not use
Mscorlib" seçeneğini asağıdaki gibi true yapın.
3- Proje özellikleri penceresinde "Common Properties" seçeneğindeki "Build Events"
kısmındaki alanları doldurarak projenin olusturulması sırasında ve proje olusturulduktan
sonra çalıstırılacak batch komutları yazılabilir. Önceden tanımlanmıs bir kaç makroyu da
kullanmak mümkündür. "Post-Build Event Command Line" seçeneği ile ilgili 3 durum
sözkonusudur. Bunlar batch komutlarının
a) Her zaman çalıstırılması
b) Yalnızca basarılı olusturmalar sırasında çalıstırılması
c) Olusturma islemi çıktı dosyalarını güncellediği zaman çalıstırılmasıdır.
Bu seçenkelerin ayarlandığı pencere asağıdaki gibidir.
4- Web ve Windows uygulamaları için birden fazla çalısma zamanı desteği sağlamak
mümkündür. Örneğin .NET Framework 1.0 ve .NET Framework 1.1 çalısma zamanını
destekleyecek ve bu ortamlarda çalısabilecek uygulama gelistirmek mümkündür. Bu ayarı
yapmak için proje özellikleri penceresindeki "Common Properties" sekmesindeki
"General" sayfasından "Supported Runtimes" özelliğini değistirmek gerekir. Bu özelliğe
tıklanıldığında asağıdaki pencere ile karsılasılır.
Bu ayar yapılırken dikkat edilmesi gereken nokta .NET Framework 1.1 'deki bazı
özelliklerin 1.0 versiyonunda bulunmamaısıdır. Bu yüzden 1.1 versiyonunda gelistirilen
projelerin 1.0 versiyonunda çalısabilmesi için ortak özelliklerin bulunması gerekir.
Yeni Eklenen "Intellisense" Özellikleri
VS.NET IDE'si gelistirici için büyük kolaylıklar sağlamaktadır. Bu kolaylıkların en bilineni
ve en ise yarayanı IDE'nin akıllı olmasıdır. IDE, dilin kurallarına göre legal olan bir çok
olayı bizim için otomatik yapmaktadır. Örneğin bir nesnenin metotlarını ve özelliklerini '.'
operatöründen sonra görebilmemiz gibi. VS.NET 2003 IDE sine hosunuza gidecek yeni
akıllı özellikler eklenmistir. Bu kısımda bu yeni özellikleri göreceğiz.
1- Göze çarpan ilk değisiklik olaylara yeni bir metot ekleme sırasında görülmektedir.
VS.NET tasarım ekranında bir kontrole ait olayı islemek istediğimizde Properties
ekranında ilgili olayı seçip metot ismini yazıyorduk. Yeni versiyonda bu islemler kod
editöründen de yapılabilmektedir. Bir olaya += operatörü ile bir metot eklemek
istediğimizde "TAB" tusuna basarsak olayı yönetecek temsilci new operatörü ile eklenir.
Tekrar "TAB" tusuna basıldığında olayı yönetecek temsilcinin temsil edeceği metodun
prototipine uygun bir "event handler" metodunun bildirimi otomatik olarak yapılır.
2- Diğer bir yeni özellik arayüzlerin türetilmesi sırasında görülür. Bildiğiniz gibi bir sınıf bir
arayüzden türetiliyorsa arayüzde bildirilmis olan bütün özellik ve metotların türeyen
sınıfta tanımlanması yani uygulanması gerekir. VS.NET IDE si türetilen arayüzdeki
eleman bildirimlerini türeyen sınıfta otomatik olarak gerçeklestirir. Asağıdaki ekran
görüntüsünde bu islemin nasıl yapıldığı gösterilmektedir.
Türetilecek arayüz ismi yazıldıktan sonra TAB tusuna basılırsa arayüzdeki eleman
bildirimleri Deneme sınıfına otomatik olarak eklenecektir. Bu islemden sonra Dispose()
metodunun bildirimi Deneme sınıfına otomatik olarak eklenmis olacaktır.
3- Türetme sırasında yeniden yüklenebilecek(overiride) metotlar override anahtar
sözcüğü yazıldıktan sonra otomatik olarak gösterilir.Örneğin Metot1 ve Metot2 adında 2
tane sanal(virtual) metodu olan TemelSınıf'tan türeyen sınıf içinde override anahtar
sözcüğü kullanıldıktan sonra asağıdaki ekran görüntüsü elde edilir.
Yukarıdaki ekranda Object sınıfının metotlarının da gösterildiğine dikkat edin. Ayrıca
TemelSınıf taki sanal olmayan metotların da gösterilmediğine dikkat edin.
4- VS.NET IDE'sinde nesneler ile '.' operatörü kullanıldığı anda nesnenin türüne ait
elemanlar listelenir. Listeleme yapılırken ilk eleman her zaman en basta olur. Yani
sıralama islemi eleman isimlerinin alfabetik sıraya göre dizilmesiyle yapılır. Yeni IDE ile
birlikte kullandığınız elemanlar "sık kullanılan elemanlar" bölümüne eklenerek bir sonraki
kullanımda en son kullanmıs olduğunuz elemanın seçili olması sağlanır. En çok
kullandığımız Console sınıfının WriteLine() metodunu örnek verelim. Listede Write()
metodu WriteLine() metodundan önce gelmektedir. Dolayısıyla WriteLine() metodunu
seçebilmek için "WriteL" yazmak gerekecektir. Oysa yeni kullanımda WriteLine()
metodunu bir kere seçtikten sonra bir sonraki kullanımda '.' operatörüne basıp "W"
yazıldığı anda WriteLine() metodu seçilecektir.
5- Diğer bir yeni özellik ise Debug islemi sırasında kullanılan "Immediate Window"
penceresinin kullanımında görülür. Artık "Immediate" penceresinde de kod editöründe
olduğu gibi nesnelerin özelliklerini ve metotlarını görebilmekteyiz. Object türünden olan s
nesnesinin üye metotlarının "Immediate" penceresinden ne sekilde görüldüğü asağıdaki
ekran görüntüsünde gösterilmistir.
Not: "Immediate" penceresi ile çalısabilmek için kaynak kodda herhangi bir satıra
"Breakpoint" yerlestirip programı "Debugger" ile birlikte derlemeniz gerekir.
Sonuç
Visual C# ve VS.NET IDE'sinde yapılan değisiklikler yukarıda anlatılanlar ile sınırlı
değildir. Ancak göze çarpan yeni özellikler bunlardır diyebiliriz. VS.NET 2003'teki diğer
göze çarpan özellik ise "MMIT" ve "Smart Device" eklentilerinin varsayılan olarak
yüklenmesidir.
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) yaklası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ıs 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 ilisikili verilerlerdir. Diğer taraftan sınıfın bizim için değisik isleri
yapmasını metodları vasıtasıyla sağlarız. Sınıf tasarımı çok önemli bir is olup; deneyim,
konsantrasyon ve dikkat ister. Sınıfımızın özelliklerini tutan veriler, program akısı
sırasında sınıf dısında değistirilebilir veya bu değerlere ulasmak istenebilir.
Bu durumda akla ilk gelen çözüm sınıfın verilerinin hepsinin dısarıdan ulasilabilmesini ve
değistirilebilmesine olanak sağlayan public anahtari ile tanımlamakdir. Asağıdaki
programda bu tür bir çözümün uygun olabileceği düsünülmüstür:
using System;
namespace Property_Makale
{
class Otomobil
{
public int model;
public string marka;
public string renk;
public Otomobil(int model, string marka, string renk)
{
if(model>DateTime.Now.Year)
this.model=DateTime.Now.Year;
else this.model=model;
this.marka=marka;
this.renk=renk;
}
public void OzellikleriGoster()
{
Console.WriteLine("\nOtomobilimizin Özellikleri: ");
Console.WriteLine("\t Marka: "+ marka);
Console.WriteLine("\t Model: "+ model);
Console.WriteLine("\t Renk: "+ renk+"\n" );
}
}
class OtomobilTest
{
static void Main(string[] args)
{
Otomobil oto1 = new Otomobil(2000, "BMW" , "Siyah");
oto1.OzellikleriGoster();
oto1.model=300;
oto1.OzellikleriGoster();
}
}
}
Yukarıdaki kod örneğimizde iki tane sınıf bulunmaktadır. Otomobil sınıfı ile otomobil
nesnelerimizi olusturabiliriz. 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 olusturmak ve onların özellikerini
çağırmak için kullanacağız. Simdi isterseniz Main() fonksiyonunu incelemeye baslayalım.
Metodun hemen ilk basında oto1 isimli nesnemizi olusturuyoruz. oto1 nesnemizin
özellikleri 2000 model, siyah ve BMW olsun. Bir sonraki satırda oto1 nesnemizin modelini
300 yapıyoruz. Đste burda büyük bir hata yapılıyor! Çünkü 300 yılında henüz otomobil
üretilmemisti. Böyle bir hatayı nasıl önleriz? Çözüm olarak otomobil nesnemizin herhangi
bir özelliğini değistirmek için ayrı bir metod yazmamız gerekir. O zaman programızı su
sekilde değistirmemiz gerekiyor:
using System;
namespace Property_Makale
{
class Otomobil
{
private int model;
public string marka;
public string renk;
public Otomobil(int model, string marka, string renk)
{
if(model>DateTime.Now.Year)
this.model=DateTime.Now.Year;
else this.model=model;
this.marka=marka;
this.renk=renk;
}
public void OzellikleriGoster()
{
Console.WriteLine("\nOtomobilimizin Özellikleri: ");
Console.WriteLine("\t Marka: "+ marka);
Console.WriteLine("\t Model: "+ model);
Console.WriteLine("\t Renk: "+ renk+"\n" );
}
public void ModelDegistir(int yeniModel)
{
if( (yeniModel>DateTime.Now.Year)||(yeniModel<1900) )
Console.WriteLine("Otomobilin modeli su an ki yildan büyük veya 1900'den
küçük olamaz ! ");
else this.model=yeniModel;
}
}
class OtomobilTest
{
static void Main(string[] args)
{
Otomobil oto1 = new Otomobil(2000, "BMW" , "Siyah");
oto1.OzellikleriGoster();
oto1.ModelDegistir(300);
oto1.OzellikleriGoster();
}
}
}
Yukarıdaki programda Otomobil sınıfına ModelDegistir(int yeniModel) metodunu ekledik.
Bu metod ile modeli değistirilecek nesnenin modelinin su anda bulunulan yıldan sonra ve
1900'den önce yapılmak istendiğinde hata mesajı veriyor ve modelini değistirmiyor.
Ayrıca sınıf içindeki model değiskenin tanımlanmasında private anahtarını da
kullandığımıza dikkat ediniz. Bu sekildeki bir yaklasım ile hem sınıfın iç isleyisini sınıf
dısından saklamıs oluyoruz hem de sınıfa ait verilerin değistirilmesini 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 gelismis bir yola sahibiz. Sınıf içindeki değerleri değistirmek ve ulasmak için
özellik (Property) aracını kullanırız. Asağıdaki program ise C#'ın özellikleri nasıl
kullandığına bir örnektir:
using System;
namespace Property_Makale
{
class Otomobil
{
private int model;
public string marka;
public string renk;
public Otomobil(int model, string marka, string renk)
{
if(model>DateTime.Now.Year)
this.model=DateTime.Now.Year;
else this.model=model;
this.marka=marka;
this.renk=renk;
}
public void OzellikleriGoster()
{
Console.WriteLine("\nOtomobilimizin Özellikleri: ");
Console.WriteLine("\t Marka: "+ marka);
Console.WriteLine("\t Model: "+ model);
Console.WriteLine("\t Renk: "+ renk+"\n" );
}
public int Model
{
get
{
return model ;
}
set
{
if((value>DateTime.Now.Year)||(value<1900) )
{
Console.WriteLine("Otomobilin modeli su an ki yildan büyük veya
1900'den küçük olamaz ! \n");
}
else this.model=value;
}
}
}
class OtomobilTest
{
static void Main(string[] args)
{
Otomobil oto = new Otomobil(2000, "BMW" , "Siyah");
Console.WriteLine("Otomobilimizin modeli: "+ oto.Model);
oto.Model=300;
oto.OzellikleriGoster();
}
}
}
Yukarıdaki programı çalıstırdığımızda asağı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ğiskenin değerini öğrenebilir ve
değistirebiliriz. Yukarıdaki örnek kodda yeralan asağıdaki kısımda bir özellik(Property)
tanımlıyoruz. Genellikle bir özelliğin ismi üzerinde is yaptığı değiskenin 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 sekilde bir kullanım bizim kodumuzu okuyanların kodu daha kolay
anlaması ve bizim baskaları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ğiskenin tipidir :-)
public int Model
{
get
{
return model ;
}
set
{
if((value>DateTime.Now.Year)||(value<1900) )
{
Console.WriteLine("Otomobilin modeli su an ki yildan büyük veya
1900'den küçük olamaz ! \n");
}
else this.model=value;
}
}
}
Ö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ğiskenimizin değerini
dısarıya döndeririz. set bloğunda ise değiskenimizin değerini değistiririz. Burda gereken
kontrolleri yapıp daha sonra uygunsa girilen değeri kabul edebiliriz. Eminimki get bloğu
içinde dikkattinizi değiskeni ismi yerine value seklinde çağırmamız çekmistir. Bu
sayede kod içinde karısıklık olmaz. Zaten sadece bir tane değisken üzerinde
çalısı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 gelistirmeye yeni bir soluk gelmis olsada C# ile eskiden
yazılmıs COM komponentlerine erisebilmek mümkündür. Daha önceki iki makalede .NET
ve COM iliskisini detaylı bir sekilde incelemistik. 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 sekilde çalısması belkide C#
ve .NET'i diğer yazılım gelistirme 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ıs ve parametre olarak gösterici alan COM komponentleri ve Windows API
fonksiyonları C# ile sorunsuz bir sekilde çalıstırılabilmektedir. Bu yazıda Win API
fonksiyonlarının .NET ortamında ne sekilde ele alındığı incelenecektir.
Win32 sistem fonksiyonları kullanıldığında, kod CLR tarafından yönetilmekten çıkar. .NET
ortamında gelistirilen bir uygulamada yönetilmeyen kod segmenti ile kasılası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ısı olur.
CLR tarafından yönetilmeyen kodlara erisebilmek 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 bası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.
Asağıdaki programda Win32 sistemlerinde bulunan MessageBox() fonksiyonunun
kullanımına bir örnek verilmistir.
Not : Bu yazıda C#'ta niteliklerin(Attributes) nasıl kullanıldığını bildiğiniz varsayılmıstır.
using System;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("user32.dll")]
public static extern int MessageBox(int tip,string mesaj,string baslik,int secenek);
static void Main()
{
MessageBox(0,"Mesaj","Win API MessageBox",2);
}
}
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
verilmistir. 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ısma seklini 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ğisen tek sey metodun bildirim
seklidir.
Not : Gösterilen mesaj kutusunun farklı formlarını görmek için MessageBox metodunun
parametreleri ile oynayın.
Simdi kısaca yukarıdaki programın çalısma zamanındaki durumunu inceleyelim. Program
çalıstı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 baslangıç adresi saklanır.
Ardından bizim parametre olarak geçtiğimiz değiskenler DLL' deki fonksiyona uyumlu hale
getirilir ve parametre olarak geçirilir. Eğer bir geri dönüs değeri bekleniyorsa
yönetilmeyen kod bölümünden gelen değer uygun .NET türüne dönüstürülerek islemlere
devam edilir. Bu islemler tamamen .NET'in alt yapısını ilgilendirmektedir. Dolayısıyla
programcının yapacağı is sadece metodun bildirimini doğru bir sekilde gerçeklestirmektir.
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çeklestirilebilmektedir. Örneğin
MessageBox fonksiyonuna TebrikMesajiVer seklinde bir takma isim vermek için asağıdaki
gibi bir bildirim yapılmalıdır.
using System;
using System.Runtime.InteropServices;
class Class1
{
[DllImport("user32.dll",EntryPoint = "MessageBox")]
public static extern int TebrikMesajiVer(int tip,string mesaj,string baslik,int secenek);
static void Main()
{
TebrikMesajiVer(0,"Tebrikler","Takma Đsim Verme",0);
}
}
Simdi 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 isaretlenmis 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
asağıda açıklanmıstır.
4 - CallingConvention : Bu paramtre CallingConvention numaralandırması türündendir.
Bu parametre ile harici metodun ne sekilde çağrılacağı ile ilgili bilgi verilir.
CallingConvention numaralandırması 5 tane sembol içerir. Varsayılan olarak bu sembol
Winapi olacak sekilde ayarlanmıstır. Yani CallingConvention parametresini DllImport ile
kullanmıyorsak varsayılan olarak bu Winapi'dir. Diğer semboller ise Cdecl, FastCall,
ThisCall, StdCall seklindedir. 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 sekilde öğ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
seklindedir. CharSet numaralandırmasının diğer sembolleri Ansi, Unicode ve None
seklindedir.
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ı tası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ısmasını istiyorsanız
programınızı değistirip 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ısma modeline ve C#'ta
kullanabileceğimiz yığın sınıfını ve bu sınıfın metodları üzerinde durmustuk. Simdi 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ısma
mantığı olarak tam tersi gibi görünmelerine rağmen bir çok
metodları ve özellikleri birebir örtüsü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ısma Sekli
Sıralar (queue) Đlk Giren Đlk Çıkar (FIFO) prensibi ile çalısan veri yapısı sekinde
bilinirler. Bir sıraya ilk giren eleman ilk olarak çıkar. Sıralara örnek olarak bir markette
alısverisini yapan müsterilerin, aldıkları ürünlerin ücretlerini ödemek için kasada sıraya
geçmeleri verilebilir. Marketteki sırada sıraya ilk giren müsterinin isi ilk önce biter. Daha
sonra ikinci ve üçüncü müsterilerin isleri yapılır.
Sıralar bilgisayar programlamada sık sık basvurulan veri yapılarıdır. Mesela, isletim
sisteminde yapılması gereken isleri bir sıra veri yapısı ile tutarız. Herhangi bir anda yeni
bir is geldiği zaman bu is sıraya (Queue) girer. Sırası gelen is yapılır ve sonraki ise 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 bas 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 isimize yarar. Bu metod
sıranın bası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 olusturalım:
Queue sira = new Queue();
// Nesnemize Enqueue metodu ile değerler girelim:
sira.Enqueue("Ahmet");
sira.Enqueue("Ferit");
sira.Enqueue("Hasan");
sira.Enqueue("Hüseyin");
// sira isimli nesnemizin eleman sayısı:
Console.WriteLine( "\n sira nesmemizin eleman sayısı: " + sira.Count);
// sira isimli nesnemizin elemanları:
Console.WriteLine( "\n sıra nesmemizin elemanları: " );
DegerleriYaz( sira );
//sira isimli nesmemizden bir eleman alalım:
string eleman= (string)sira.Dequeue();
Console.WriteLine(" \n Sıramizin basından sunu aldık: " + eleman);
//simdi ise siranin en basındaki nesneyi öğrenelim.
// Ama onu ıiradan çıkartmayacağız:
eleman= (string)sira.Peek();
Console.WriteLine(" \n Sıramızın basındaki eleman " + eleman);
}
public static void DegerleriYaz( IEnumerable kolleksiyon )
{
System.Collections.IEnumerator Enum = kolleksiyon.GetEnumerator();
while ( Enum.MoveNext() )
Console.Write( "\t{0}", Enum.Current );
Console.WriteLine();
}
}
Yukarıdaki örnek programda önce Queue sınıfından sıra isimli bir nesne olusturuyoruz.
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ğiskenlerin
değerlerini öğrenmemize ve onların değerlerini değistirmemize 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ı bosaltmak/temizlemek istersek Clean() metodu isimizi yarayacaktır. Clean()
metodu hiçbir parametre almaz ve hiçbirsey 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 asağı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 olusturalım:
Queue sira = new Queue();
// Nesnemize Enqueue metodu ile değerler girelim:
sira.Enqueue(12);
sira.Enqueue(3);
sira.Enqueue(18);
sira.Enqueue(7);
sira.Enqueue(20);
// sıra isimli nesnemizin eleman sayısı:
Console.WriteLine( "\n sira nesmemizin eleman sayısı: " + sira.Count);
// sira isimli nesnemizin elemanları:
Console.WriteLine( "\n sıra nesmemizin elemanları: " );
DegerleriYaz( sira );
//Contains metodunun kullanımı:
int sayi=7;
if(sira.Contains(7))
Console.WriteLine("Sıramızda " + sayi + " var.");
else
Console.WriteLine("Sıramızda " + sayi + " yok.");
// sıramızın içindeki elemanları silelim:
sira.Clear();
// Sıramızı temizledikten sonra kaç tane
//eleman bulunduğunu bulup yazalım.
int elemanSayisi= sira.Count;
Console.WriteLine("\n Sıramizda su anda {0} tane eleman vardır.", elemanSayisi);
}
public static void DegerleriYaz( IEnumerable kolleksiyon )
{
System.Collections.IEnumerator Enum = kolleksiyon.GetEnumerator();
while ( Enum.MoveNext() )
Console.Write( "\t{0}", Enum.Current );
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ısmalar.
Visual C# ile Basit Bir Not Defteri Uygulaması
Bu yazımızda konu olarak Windows Form’u seçtim;çünkü bu konuda Türkçe kaynak
neredeyse yok, doğru dürüst bir programın yapımını gösteren bir yazı, bir site
bulamadım, tabi ki Türkçe bir çok makale var sitelerde, bende onlardan yararlanarak ve
deneyerek bir seyler yaptım ve simdi bu yaptıklarımı sizinle paylasıyorum.
Eğer bu yazıyı sonuna kadar okursanız ve kodları sizde yazarsanız, yazının sonuna
geldiğiniz Basit Not Defteri adında bir uygulamanız olacak. Önce bu programdan biraz
bahsedelim. Adı üstünde bir Not Defteri uygulaması ancak basit hem de çok basit.
Yapabildiği seyler: Yeni dosya yaratmak, var olan dosyaları açmak, dosya kaydetmek…
Böyle bir program yapmamın sebebi tabi ki metin editörleri konusunda alternatif
olusturma isteği falan değil, tek sebep benim ilk baslarda çok zorlandığım SaveFileDialog,
OpenFileDialog gibi kontroller konusunda örneklemeler yapmak..
Lafı daha fazla uzatmadan artık uygulamaya geçelim. Önce asağıdaki programı Visual
Studio .Net’in Designer’ında olusturun…
Ben bu resime kullandığım kontrollerin isimlerini de yazdım ki kodları incelerken zorluk
çıkmasın. Yalnız burada görünmeyen 2 kontrol daha var. Biri SaveFileDialog (objSave),
diğeri OpenFileDialog (objOpen). Bu kontrolleri de ekleyip adlarını parantez içlerindeki
gibi yaparsanız sorun çıkmaz…
Simdi kodlarımıza geçebiliriz. Bu bölümde adım adım ilerleyeceğiz. Menülerdeki tüm
baslıkların olaylarını yazacağız.
1)Genel değiskenimizi tanımlama
Bu programda Degisim adında, “bool” yani sadece true ve false değerleri alabilen bir
değisken tanımladım. Bu değisken sayesinde kullanıcıyı metinin değisip değismediği
konusunda uyaracağız, böylece isterse değisen metni kayıt imkanı vereceğiz…
private bool Degisim;
2)Form1’in onLoad Olayı
Bu olay programımızın açılısında yürütülen olaydır. Burada objSave ve objOpen için bazı
ayarlar yapıyoruz ve göstermesini istediğimiz dosyaların uzantılarını giriyoruz.
private void Form1_Load(object sender, System.EventArgs e)
{
objOpen.Filter = "Text Dosyaları(*.txt)|*.txt|Tüm Dosylar(*.*)|*.*" ;
objOpen.FilterIndex = 1 ;
objSave.Filter = "Text Dosyaları(*.txt)|*.txt|Tüm Dosyalar(*.*)|*.*" ;
objSave.FilterIndex = 1 ;
}
3)Kullanılacak metotların tanımlanması…
Ben bu programda sadece 2 tane metot tanımladım. Bunlardan birincisi
KayitMekanizmasi, diğeri DegisimUyari . KayitMekanizmasi adı üstünde yazdıklarımızı
kaydedecek olan mekanizma, DegisimUyari ise objText içindeki metnin değisimi durumda
programı kapatırken falan bize haber verecek olan kod.
public void KayitMekanizmasi(string strVeri)
{
if (objSave.ShowDialog() == DialogResult.OK)
{
StreamWriter Kayitci = new
StreamWriter(Environment.GetEnvironmentVariable("mydocuments")+objSave.FileNam
e.ToString(),false,System.Text.Encoding.Unicode);
Kayitci.Write(strVeri);
Kayitci.Close();
Degisim = false;
}
}
Önce yukarıdaki kodu biraz inceleyelim. Burada önce bi if kontrolü görüyorsunuz. Bu
kontrolün amacı, Kayıt ekranı açıldığı zaman kullanıcı “OK” düğmesine tıklayıp
tıklamadığını kontrol etmek. Eğer “OK”e tıkladı ise programımız bir adet StreamWriter
olusturuyor. Kayitci adındaki bu Writer
Environment.GetEnvironmentVariable("mydocuments”) bu kod ile ayarlı olan Belgelerim
klasörüne gidiyor otomatik olarak. objSave.FileName ise bizim Kayıt Ekranın da dosyaya
verdiğimiz ismi bize döndürüyor. Son olarak ise bu satırda Unicode bir kodlama
yaptığımız gösteriyoruz. Bunu yazmazsanız Türkçe karakterlerinizin yerinde yeller estiğini
görürsünüz.
Kayitci.Write(strVeri) satırı ile gelen veriyi kaydediyor ve StreamWriter nesnesini
kapatıyor. Degisim değerini ise true olarak atıyor. Bunun nedeni değisim oldu ve ben
bunu gördüm demek. Kullanıcıya haber vermeye gerek yok anlamına gelecek.
Simdi devam edelim.
public bool DegisimUyari()
{
if (MessageBox.Show("Dosyanızda bir değisiklik oldu kaydetmek ister
misiniz?","Değisiklik Var",MessageBoxButtons.YesNo,MessageBoxIcon.Exclamation) ==
DialogResult.Yes)
{
return true;
}
else
{
Degisim = false;
return false;
}
}
Yukarıdaki kodda ise tipik bir MessageBox kullanımı görüyorsunuz. Buradaki metodumuz
birde değer döndürüyor.Bir bool değeri döndürüyor. Bu dönen değer ile biz az sonra
kullanıcının çıkan mesaj kutusunda dosyayı kaydetmek isteyip istemediğini anlayacağız.
MessageBox.Show("Dosyanızda bir değisiklik oldu kaydetmek ister misiniz?","Değisiklik
Var",MessageBoxButtons.YesNo,MessageBoxIcon.Exclamation) == DialogResult.Yes)
Bu satırı biraz incelemek lazım. Burada ilk overload (Overload metodlara parantezler
içinde yollanan veri demek.) mesaj kutusunda görünecek olan yazı, ikinicisi bu mesaj
kutusunun baslığı, üçüncüsü mesaj kutusu üzerinde ki “Evet”, “Hayır” düğmeleri ve son
olarak mesaj kutusundaki simge. Ancak kodlara bakmaya devam ettiğimizde bir
karsılastırma görüyoruz (“==” ifadesi) DialogResult.Yes , aslında açıklamaya bile gerek
yok. Eğer kullanıcı “Evet”e tıkladı ise demek. Asıl kodlarda bu durumda bir “true” ifadesi
döndürüldüğünü görebilirsiniz. Biz daha sonra bunu kontrol ederek KayitMekanizmasi
metodumuzu çağıracağız.
4) Yeni düğmesi
Menümüzdeki “Yeni” düğmesine tıkladığımızda olacak olayları gireceğiz. Bunun için bu
düğmeye Designer’dan çift tıklayınız.
private void menuItem2_Click(object sender, System.EventArgs e)
{
if (Degisim == false)
{
objText.Clear();
}
else
{
if (DegisimUyari())
{
KayitMekanizmasi(objText.Text);
objText.Clear();
Degisim = false;
}
else
{
objText.Clear();
Degisim = false;
}
}
}
Burada önce Degisim değerini kontrol ediyoruz. Eğer değer “false” ise yani değisim yoksa
ya bu dosya önceden kaydedilmistir ya da yeni açılmıstır. O zaman içeriğinin
temizlenmesinde bir sorun yok.
Eğer değer “true” ise biraz karısıyor ortalık. Önce kullanıcıyı uyarmak için DegisimUyari()
çalıstırılıyor. Eğer kullanıcı kayıt etmek istiyorsa, KayitMekanizmasi() çalıstırılıyor, ekran
temizleniyor ve Degisim değeri false oluyor.Eğer kullanıcı kayıt etmek istemiyorsa içerik
temizleniyor ve Degisim değeri yine false oluyor. Böylece yeni bir dosya açma islemlerini
hallettik.
5) Varolan dosyayı açma
Metin editörünüz ile daha önce var olan bir dosyayı açmak istersiniz diye böyle bir özellik
ekledik birde. Menümüzde “Aç”a çift tıklayın ve tıklama olayına asağıdaki kodları girin.
private void menuItem3_Click(object sender, System.EventArgs e)
{
if (Degisim == true)
{
if (DegisimUyari())
{
KayitMekanizmasi(objText.Text);
}
}
if (objOpen.ShowDialog() == DialogResult.OK)
{
FileInfo strKaynak = new
FileInfo(Environment.GetEnvironmentVariable("mydocuments")+objOpen.FileName.ToS
tring());
StreamReader Okuyucu = strKaynak.OpenText();
objText.Text = Okuyucu.ReadToEnd();
Degisim = false;
Okuyucu.Close();
}
}
Bu kodlarda da önce değisim var mı diye bakıyoruz. Yani amacımız kullanıcının yazdığı
metni yanlıslıkla bastığı bir düğme yüzünden kaybetmesini engellemek. Eğer değisim
varsa ve uyarıdan “true” değeri dönerse kaydediyoruz, aksi halde herhangi bir sey
yapmıyoruz.
Bundan sonra yukarıda SaveFileDialog için yaptığımız benzer seyleri yapıyoruz. Yani
.ShowDialog() metodunu çağırıyoruz. Kullanıcı OK’e tıklayınca kodlarımız devam ediyor.
Ancak burada yukarıdakinden farklı kodlar var. Dosya okumak için çok farklı yöntemler
var. Yazmak içinde tabi ki. Mesela StreamWriter’ın StreamReader’ı da var ve ben burada
bunu kullandım. Eğer kodları incelerseniz biraz farklı olduğunu göreceksiniz. Çünkü
burada FileInfo diye de bir sey var. FileInfo bu tür dosya islemcilerine yardımcı olur.
strKaynak değiskenine atadığımız nesnemizde StreamWriter daki gibi path gösterip
dosyamızı açıyoruz. Burada objOpen.FileName’den gelen veri, kullanıcının açmak istediği
dosya.
StreamReader nesnesini de olusturup strKaynak.OpenText() ile metin dosyamızı
açıyoruz. Yalnız burada bir noktaya dikkat çekmek istiyorum. Ben burada açılacak
dosyanın bir .txt dosyası olduğunu bildiğim için .OpenText’i kullandım. Yoksa baska
versiyonları da mevcut. Bu nesneyi de olusturduktan sonra objText’e
Okuyucu.ReadToEnd ile bastan sonra tüm veriyi okuyup aktarıyoruz. Değisimden
haberimiz olduğunu programa bildirip, nesnelerimizi kapatıyoruz…
6) Kaydet düğmesi
Kullanıcı çalısmasını kaydetmek istediği zaman bu düğmeye tıklayabilir. Çok kısa bir kodu
var. Zaten asıl isi yapan KayitMekanizmasi(), biz sadece onu çağıracağız simdi.
private void menuItem4_Click(object sender, System.EventArgs e)
{
KayitMekanizmasi(objText.Text);
Degisim = false;
}
Burada açıklanacak bir kod yok. Gördüğünüz gibi …
7) Kapat Düğmesi
Kullanıcı programı kapatmak isteyebilir ve bunun için Dosya menüsündeki Kapat
düğmesini kullanabilir. O zaman bu düğmeye de bir olay atamamız lazım. Simdi çift
tıklayın ve asağıdaki kodları yazın.
private void menuItem6_Click(object sender, System.EventArgs e)
{
Close();
}
Bu kod çok basit. Sadece Close() metodunu çağırıyor. Bu özel tanımlı bir metodur ve o
form penceresinin kapanmasını sağlar. Simdi aklınıza gelebilir ya içeride kaydedilmemis
veri varsa hiç kontrol etmedik. O zaman biraz sabır, ona da bakacağız…
8) Kapanmadan önce kontrol
Kullanıcımız programı çok farklı sekillerde kapatabilir. Alt + F4 kombinasyonu, kösedeki X
ile kapatabilir ya da Kapat düğmemize tıklar;ancak az önce de dediğimiz gibi ya içeride
veri varsa. O zaman bu veri için bi kontrol yapmamız lazım. Form’ların “Closing” adında
olayları vardır. Bu form kapatılmadan hemen önce yapılacakları belirler. Biz buna bazı
olaylar atıyoruz simdi.
private void Form1_Closing(object sender,
System.ComponentModel.CancelEventArgs e)
{
if (Degisim == true)
{
if (DegisimUyari())
{
KayitMekanizmasi(objText.Text);
Close();
}
}
else
{
Close();
}
}
Burada yapılanlardan farklı olan hiç bir sey yok. Degisim değerini kontrol ediyoruz ve ona
göre islem yapıyoruz..
9) Son bir metod…
Asıl en önemli seyi yapmadık sanıyorum. Örneğin kullanıcı programa bir veri girdiğinde
yani herhangi bi yazı yazdığında Degisim değeri değismedi. O zaman bunu halledelim.
objText’in TextChanged adında bir olayı var. Simdi o olay kodları içine asağıdaki tek
satırlık kodu yazıyoruz.
private void objText_TextChanged(object sender, System.EventArgs e)
{
Degisim = true;
}
Evet, böylece olayın is yapan kısmı bitti..
Ancak menülerimiz arasında hiç ilgilenmediğimiz bir düğme var. Hakkında. Bu aslında en
gereksiz sey belki ama bir programcının en çok önemsediği bölüm :). Bunun için basit bir
form yaratınız. Ben asağıdaki formu olusturdum ve adını “hakkinda” yaptım.
Burada altta iki tane de link var. Biri MaxiASP.Com ‘a biri MaxiASP.Net’e yönlenmis
durumda. Bunlara tıklandığında tarayıcımızın açılıp sitelere gitmemizi sağlayacak kodlarda
asağıda.
private void linkLabel1_LinkClicked(object sender,
System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("http://www.maxiasp.com");
}
private void linkLabel2_LinkClicked(object sender,
System.Windows.Forms.LinkLabelLinkClickedEventArgs e)
{
System.Diagnostics.Process.Start("http://www.maxiasp.net");
}
Gerçekten çok uzun bir yazı oldu. Eğer her sey yolunda gitti ise su an canavar gibi çalısan
bir “Basit Not Defteriniz” var. Baska yazılarda görüsmek üzere.
C#'ın Gelecekteki Özellikleri
Bildiğiniz gibi C# dili 2001 yılında Microsoft tarafından çıkarılan ve nesne yönelimli
programlama tekniğine %100 destek veren bir programlama dilidir. C#, programcılara
sunulduğundan beri bir çok programcının dikkatini çekmistir. Bu ilgide en önemli neden
herhalde C# dilinin kendinden önce çıkarılmıs olan JAVA ve C++ dillerini örnek almasıdır.
Evet C# modern çağın gerektirdiği bütün yazılım bilesenlerini içermekle beraber eski
programlama dillerinde bulunan iyi özellikleri de yapısında barındırmaktadır. Microsoft ve
C# dil tasarımcıları her geçen gün yeni piyasa arastırmaları yaparak dile katabilecekleri
özellikleri tartısmaktadırlar. Bu amaçla C# dilinin tasarımcıları yakın bir zaman içinde C#
diline eklemeyi düsündükleri yeni özellikleri bildirmislerdir. Bu yazıda muhtemelen
"VS.NET for Yukon(VS.NET Everett'ten sonraki versiyon)" ile birlikte uygulamaya
konulacak C# dilinin muhtemel özelliklerini özetlemeye çalısacağım. Bu bildirinin
tamamını C# topluluğunun resmi sitesi olan www.csharp.net adresinden okuyabilirsiniz.
C# diline yakın bir zamanda eklenilmesi düsünülen özellikler 4 ana baslık altında
toplanmıstır. Bu özellikler temel olarak asağıdaki gibidir.
1 - Generics (Soysal Türler)
2 - Iterators
3 - Anonymous Methods (Đsimsiz-Anonim- Metotlar)
4 - Partial Types (Kısmi Türler)
Bu yazıda yukarıda baslıklar halinde verilen her bir konuyu ayrıntılı olarak inceleyip,
programcıya ne gibi faydalar sağlayabileceğini ve programların performansına nasıl etki
edeceğine değineceğim.
1 - Generics
Profesyonel programlamada, türden bağımsız algoritma gelistirme önemli bir tekniktir.
Türden bağımsız algoritmalar gelistirici için büyük kolaylıklar sağlamaktadır. Söz gelimi iki
int türden sayının toplanmasının sağlayan bir fonksiyonu yazdıktan sonra aynı islemi iki
double türden sayı için tekrarlamak zaman kaybına sebep olacaktır. C++ dilinde türden
bağımsız algoritma kurabilmek için sablon(template) fonksiyonları ve sablon sınıfları
kullanılmaktadır. C#, türden bağımsız algoritma gelistirmeye doğrudan destek vermiyor
olsada dolaylı yollardan türden bağımsız islemler yapabilmek mümkündür. Bu islemler
C#'ta "Her sey bir Object'tir" cümlesinin altında yatan gerçekle halledilmektedir. C#'ta
herseyin bir nesne olması ve her nesnenin ortak bir atasının olması ve bu atanın da
Object sınıfı olması bu cümlenin altında yatan gerçektir. Dolayısıyla herhangi bir türe ait
referansı Object referasnlarına ataybiliriz. Yani bir bakıma türden bağımsız bir islem
gerçeklestirmis oluyoruz. Söze gelimi Object türünden bir parametre alan bir fonksiyonu
dilediğimiz bir nesne referansı geçebiliriz. Temel(base) sınıfa ait referanslara
türeyen(inherited) sınıf referanslarını ataybilmek nesne yönelimli programlama tekniğinin
sunduğu bir imkandır.
C#'ta Object referanslarına istenilen türden referanslar atanabilir. Bu, büyük bir imkan
gibi görünsede aslında bazı dezavantajlarıda beraberinde getiriyor. Çünkü çalısma
zamanında Object türüne atanmıs referanslar orjinal türe tekrar geri dönüstürülmektedir.
Kısaca unboxing olarak bilinen bu islem özellikle değer(value) ve referans(reference)
türleri arasında yapıldığında önemsenecek büyüklükte bir performans kaybı meydana
gelmektedir. Çünkü değer ve referans türleri belleğin farklı bölgelerinde saklanmaktadır.
Bu durum boxing ve unboxing islemlerinin çalısma zamanında farklı bellek bölgeleri
arasında uzun sürebilecek veri transferlerine sebep olur. Bu tür bir performans kaybını
bazı veri yapıları için önlemek için C# dil tasarımcıları Generics isimli bi kavramın dile
eklenmesini öngörmüslerdir. Bu sayede bazı veri yapılarında özellikle .NET sınıf
küyüphanesindeki System.Collections isim alanında bulunan veri yapılarında epeyce
performans kazancı elde edilecektir.
Đsterseniz basit bir yığın(stack) sınıfı üzerinden "generics" kavramının sağlayacağı yaraları
ve boxing/unboxing islemlerinin etkisini inceleyelim.
.NET sınıf kütüphanesinde de bulunan Stack sınıfı içinde her türden veri bulunduran ve
istenildiğinde bu verilere FIFO(Đlk giren ilk çıkar) algoritmasına göre veri çekilebilen bir
veri yapısdır. .NET'teki Stack sınıfı ile bütün veri türlerine ait islemleri yapabilmek için
Stack sınıfındaki veri yapısı Object olarak seçilmistir. Eğer bu böyle olmasaydı Stack
sınıfının her bir tür için ayrı ayrı yazılması gerekecekti. Bu mümkün olsa bile hersey
bitmis olmayacaktı. Çünkü Stack sınıfı kullanıcını tanımlayacağı türleri barındıracak
duruma gelmez. Đste bütün bu sebeplerden dolayı Stack veri yapısında saklanan veriler
Object olarak seçilmistir. Buna göre Stack sınıfının arayüzü asağıdaki gibidir.
class Stack
{
private int current_index;
private object[] elemanlar = new object[100];
public void Push(object veri)
{
.
.
elemanlar[current_index] = veri;
.
.
}
public object Pop()
{
.
.
return elemanlar[current_index];
.
.
}
}
Bildirilen bu Stack sınıfının elemanı Object türünden olduğu için Push() metodu ile
istediğimiz türden veriyi saklayabiliriz. Aynı sekilde Pop() metodu ile bir veri çekileceği
zaman veri Object türünden olacaktır. Pop() metodu ile elde edilen verinin gerçek türü
belli olmadığı için tür dönüstürme operatörü kullanılır. Örneğin,
Push(3);
seklinde yığına eklenen veriyi tekrar elde etmek için
int a = (int)Pop();
biçiminde bir tür dönüsümü yapmamız gerekir. Bu islemler kendi tanımlayacağımız özel
sınıflar içinde geçerlidir. Ancak int ve double gibi temel veri türlerindeki performans kaybı
daha fazladır. Çünkü Push(3) seklindeki bir çağrımda boxing islemi gerçeklesirken Pop()
metodunun çağrılmasında unboxing islemi gerçeklesir. Üstelik bu durumda Pop()
metodunun geri dönüs değerini byte türüne dönüstürmeye çalısırsak derleme zamanında
herhangi bir hata almayız. Bu da çalısma zamanında haberimiz olmadan bazı veri
kayıplarının olabileceğini gösterir. Kullanıcı tanımlı sınıflar ilgili bir yığın kullanıyorsak
Pop() metodunun geri dönüs değerini farklı bir kullanıcı tanımlı sınıfa çaviriyorsak bu
sefer de derleme zamanında hata alınmaz, ancak çalısma zamanında "invalid cast
operation" istisnai durumu meydana gelir.
Bütün eksi durumlardan kurtulmak için generics(soysal tür)'lerden faydalanılabilir. Soysal
türler C++ dilindeki sablon sınıflarının bildirimi ile benzerdir. Bu tür sınıf bildirimlerine
parametreli tip de denilmektedir. Parametreli tipler asağıdaki gibi bildirilir.
class Stack
{
private int current_index;
private Veri türü[] elemanlar;
public void Push(Veri türü veri)
{
.
.
elemanlar[current_index] = veri;
.
.
}
public Veri türü Pop()
{
.
.
return elemanlar[current_index];
.
.
}
}
ile stack sınıfnın hangi türden verileri tutacağı stack nesnesini olusturacak programcıya
bırakılmıstır. Örneğin int türden verileri saklayacak bir yığın asağıdaki gibi olusturulur.
Stack<int> yıgın = new Stack<int>;
Yukarıdaki sekilde bir yıgın olustrulduğunda Stack sınıfınuın bildirimindeki Veri türü
ifadeleri int türü olarak ele alınacaktır. Dolayısıyla Pop() metodu ile yığından bir eleman
çıkarılıp asağıdaki gibi baska bir değiskene atanmak istendiğinde tür dönüstürme
operatörünü kullanmaya gerek yoktur. Bu da boxing ve unboxing islemlerinin
gerçeklesmediği anlamına gelir ki istediğimiz de buydu zaten.
Stack<int> yıgın = new Stack<int>;
yıgın.Push(3); // Boxing islemi gerçeklesmez.
int a = yıgın.Pop(); //Unboxing islemi gerçeklesmez.
Aynı sekilde yığınımızın double türden verileri saklamasını istiyorsak int yerine double
kullanmalıyız. Bu durumda çalısma zamanında hem int hem de double verileri tutan yığın
sınıfları olusturulacaktır. Biz tek bir yığın sınfı bildirmis olmamıza rağmen çalısma zamanı
bizim için ayrı iki yığın sınıfı olusturur.
Soysal türleri kendi tanımladığımız sınıflar içinde olusturabiliriz. Örneğin Musteri isimli bir
sınıfın verilerini yığında tutmak için yığın sınıfını asağıdaki gibi olusturmalıyız.
Stack yıgın = new Stack;
Bu durumda yığına sadece Musteri nesneleri eklenebilir. Yani yıgın.Push(3) seklindeki bir
kullanım derleme asamasında hata verecektir. Aynı zamanda yığından çekilecek veriler
de Musteri türündendir. Dolayısıyla tür dönüsümü uygun türler arasında olmalıdır.
Yığın sınıfı yukarıda anlatılan sekilde kullanıldığında yığındaki elemanların belirli bir türden
olduğu garanti altına alınır. Böylece Musteri türünden nesneleri tutan bir yığına "3" gibi
bir sayıyı ekleyemeyeceğimiz için daha gerçekçi programlar yazılır.
Stack örneğinde sadece bri tane parametre türü kullandık. Soysal türlerde istenilen
sayıda parametreli tür kullanılabilir. Örneğin Hashtable sınıfnındaki Deger ve Anahtar
ikilisi asağıdaki gibi parametreli tür olarak bildirilebilir.
public class Hashtable
{
public void Add(AnahtarTuru anahtar, DegerTuru deger)
{
.....
}
public DegerTuru this[AnahtarTuru anahtar]
{
.....
}
}
Yani bir Hashtable nesnesi olusturulacağı zaman her iki parametre türü de belirtilmelidir.
Örneğin Anahtar türü int olan ve değer türü Musteri sınıfı olan bir Hashtable nesnesi
asağıdaki gibi olusturulabilir.
Hashtable<int,Musteri> hashtable = new Hashtable<int,Musteri>;
Not : Parametre sayısını aralarına virgül koyarak dilediğimiz kadar artırabiliriz.
Soysal türlerin saydığımız avantajlarının yanında bu haliyle bazı dezavantajları ve
kısıtlamalarıda vardır. Söz gelimi Hashtable sınıfının bildirimi içinde AnahtarTuru verisinin
bazı elemanlarını bir ifade de kullanmak istiyoruz; derleyici hangi AnahtarTuru
parametrelei türünün hangi türden olduğunu bilmediği için bu durumda sadece Object
sınıfının ait metotlar ve özellikler kullanılabilir. Mesela Hashtable sınıfının Add metodu
içinde anahtar parametresi ile CompareTo() metodunu kullanmak istiyorsak CompareTo
metodunun bildirildiği IComparable arayüzünü kullanarak asağıdaki gibi tür dönüsümü
yapmalıyız.
public class Hashtable
{
public void Add(AnahtarTuru anahtar, DegerTuru deger)
{
switch(((IComparable)anahtar).CompareTo(x))
{
}
}
}
Hashtable sınıfının Add() metodu yularıdaki sekilde bildirilse bile hala eksik noktalar var.
Mesela AnahtarTuru parametresi eğer gerçekten IComparable arayüzünü uygulamıyorsa
switch ifadesi içinde yapılan tür dönüsümü geçersiz olacaktır ve çalısma zamanında hata
olusacaktır. Çalısma zamanında meydana gelebilecek bu tür hataları önlemek için
yapılabilecek tek sey AnahtarTuuru parametresinin IComparable arayüzünü uyguluyor
olmasını zorlamaktır. Bu islemi yapmak için AnahtarTuru parametresine çesitli
kısıtlar(constraints) getirilir. Asağıdaki Hashtable sınıfında AnahtarTuru parametresinin
IComparable arayüzünü uygulaması gerektiği söylenmektedir. Bu kısıt için where
anahtar sözcüğü kullanılır.
public class Hashtable<AnahtarTuru, DegerTuru> where AnahtarTuru : IComparable
{
public void Add(AnahtarTuru anahtar, DegerTuru deger)
{
switch(anahtar.CompareTo(x))
{
}
}
}
Dikkat ettiyseniz uygulanan kısıttan sonra switch ifadesi içinde anahtar değiskeni
üzerinde tür dönüsümü islemi yapmaya gerek kalmamıstır. Üstelik kaynak kodun
herhangi bir noktasında Hashtable nesnesini IComparable arayüzünü uygulamayan bir
AnahtarTuru parametresi ile olusturursak bu sefer ki hata derleme zamanında
olusacaktır.
Not : parametreli türler üzerindeki kısıt sadece arayüz olmak zorunda değildir. Arayüz
yerine sınıflar da kısıt olarak kullanılabilir.
Bir parametreli türe birden fazla arayüz kısıtı konabileceği gibi aynı sınıftaki diğer
parametreleri türler için de kısıt konulabilir. Ancak bir parametreli tür için ancak sadece
bir tane sınıf kısıt olabilir. Örneğin asağıdaki Hashtable sınıfında DegerTuru Musteri
sınıfından tremis olması gerekirken, AnahtarTuru hem IComparable hemde IEnumerable
arayüzünü uygulamıs olması gerekir.
public class Hashtable<AnahtarTuru, DegerTuru> where
AnahtarTuru : IComparable
AnahtarTuru : IEnumerable
DegerTuru : Musteri
{
public void Add(AnahtarTuru anahtar, DegerTuru deger)
{
switch(anahtar.CompareTo(x))
{
}
}
}
2 - Iterators
Bir dizinin elemanları üzerinde tek tek dolasma islemine iterasyon denilmektedir.
Koleksiyon tabanlı nesnelerin elemanları arasında tek yönlü dolasmayı sağlayan foreach
döngü yapısının bizim tanımlayacağımız sınıflar için de kullanılabilmesi için sınıfımızın bazı
arayüzleri uyguluyor olması gerekir. foreach döngüsü derleme islemi sırasında while
döngüsüne dönüstürülür. Bu dönüstürme islemi için IEnumerator arayüzündeki
metotlardan ve özelliklerden faydalanılmaktadır. Bu dönüstürme isleminin nasıl
yapıldığına bakacak olursak :
ArrayList alist = new ArrayList();
foreach(object o in alist)
{
BiseylerYap(o);
}
// Yukarıdaki foreach bloğunun karsılığı asağıdaki gibidir.
Enumerator e = alist.GetEnumerator();
while(e.MoveNext())
{
object o = e.Current
BiseylerYap(o);
}
foreach döngüs yapısı için gerekli olan arayüzlerin uygulanması özellikle ağaç yapısı
seklindeki veri türleri için oldukça zordur. Bu yüzden C# sınıfların foreach yapısı ile nasıl
kullanılacağına karar vermek için yeni bir yapı kullanacaktır.
Sınıflarda, foreach anahtar kelimesi bir metot ismi gibi kullanılarak sınıfın foreach
döngüsünde nasıl davranacağını bildirebilriz. Her bir iterasyon sonucu geri döndürülecek
değeri ise yield anahtar sözcüğü ile belirtilir. Örneğin her bir iterasyonda farklı bir
tamsayı değeri elde etmek için sınıf bildirimi asağıdaki gibi yapılabilir.
public class Sınıf
{
public int foreach()
{
yield 3;
yield 4;
yield 5;
}
}
Yukarıda bildirilen Sınıf türünden nesneler üzerinde foreach döngüsü kullanıldığında
iterasyonlarda sırasıyla 3,4 ve 5 sayıları elde edilecektir. Buna göre asağıdaki kod parçası
ekrana 345 yazacaktır.
Sınıf deneme = new Sınıf();
foreach(int eleman in deneme)
{
Console.Write(eleman);
}
Çoğu durumda foreach yapısı ile sınıfımızın içindeki bir dizi üzerinde iteratif bir sekilde
dolasmak isteyeceğiz. Bu durumda foreach bildirimi içinde ayrı bir foreach döngüsü
asağıdaki gibi kullanılabilir.
public class Sınıf
{
private int[] elemanlar;
public int foreach()
{
foreach(int eleman in elemanlar)
{
yield eleman;
}
}
}
Yukarıdaki Sınıf nesneler ile foreach döngüsü kullanıldığında her bir iterasyonda
elemanlar dizisinin bir sonraki elemanına ulasılır.
Gördüğünüz gibi programcının bildireceği sınıflar da foreach döngüs yapısını
kullanabilmek için eskiden olduğu gibi IEnumerator arayüzün uygulamaya gerek
kalmamıstır. Bu islemi derleyici bizim yerimize yapar.
3 - Anonymous Metotlar(Đsimsiz Metotlar)
Đsimsiz metotlar, bir temsilciye iliskin kod bloklarını emsil eder. Bildiğiniz gibi temsilciler
yapısında metot referasnı tutan veri yapılarıdır. Bir temsilci çağrımı yapıldığında
temsilcinin temsil ettiği metot çalıstırılır. Özellikle görsel arayüzlü programlar yazarken
event tabanlı programlama tekniği kullanılırken temsilcilerin kullanımına sıkça rastlanır.
Örneğin bir Button nesnesine tıklandığında belirli bir kod kümesinin(metot) çalıstırılması
için temsilci veri yapısından faydalanılır. Sözgelimi Button nesnesinin tıklanma olayı
meydana geldiğinde Click isimli temsilcisine yeni bir temsilci atanır. Ne zaman button
nesnesinin Click olayı gerçeklesse ardından hemen temsilcinin temsil ettiği metot çağrılır.
Buna bir örnek verecek olursak;
public class Form
{
Button dugme;
public Form
{
dugme = new Button();
dugme.Click += new EventHandler(OnClick);
}
void OnClick(object sender, EventArgs e)
{
....
}
}
Yukarıdaki koddan da görüldüğü üzere temsilci ile temsilcinin temsil ettiği metotlar ayrı
yerlerdedir. Đsimsiz metotlarla bu islemi biraz daha basitlestirmek mümkündür. Temsilci
olusturulduktan sonra açılan ve kapanan parantezler arasına temsilci çağrıldığında
çalıstırılacak kodlar yazılabilir. Yukarıdaki örneği isimsiz metot ile yapacak olursak :
public class Form
{
Button dugme;
public Form
{
dugme = new Button();
dugme.Click += new EventHandler(object sender, EventArgs e);
{
//çalıstırılacak kodlar.
};
}
}
Tanımlanan kod bloğundan sonra noktalı vürgülün eklenmis olduğuna dikkat edin.
Temsilci bloğundaki kodlar normal metotlardan biraz farklıdır. Normal kod blokları ile
benzer özellikler tasır. Yukarıdaki temsilci kod bloğunda, blok dısında tanımlanan
değiskenlere erisebilmek mümkündür. Ayrıca olay argümanlarının da(sender,e)
EventHandler türünün parantezleri içinde yazıldığınıda dikkat edin. Bir önceki versiyonda
olay argümanlarının yerine temsil edilen metodun ismi yazılmıstı.
Peki isimsiz metotlar nasıl çalıstırılmaktadır? Đsimsiz metot tanımı ile karsılasan derleyici
tekil isme sahip bir sınıf içinde tekil isme sahip bir metot olusturur ve isimsiz metot
gövdesindeki kodlara bu tekil metot içinden erisilir. Temsilci nesnesi çağrıldığında,
derleyicinin ürettiği bu metot ile isimsiz metodun bloğundaki kodlar çalıstırılır.
4 - Partial Types (Kısmi Türler)
Kısmi türler yardımıyla bir sınıfın elemanlarını farklı dosyalarda saklamak mümkündür.
Örneğin Dosya1.cs ve Dosya2.cs asağıdaki gibi olsun.
//Dosya1.cs
public partial class deneme
{
public void Metot1
{
...
}
}
//Dosya2.cs
public partial class deneme
{
public void Metot2
{
...
}
}
Yukarıdaki iki dosyayı aynı anda derlediğimizde eğer kısmi türler kavramı olmasaydı
derleme zamanında hata alırdırk. Çünkü aynı isim alanında birden fazla aynı isimli sınıf
bildirimi yapılmıs. Halbuki kısmi türler ile bu iki sınıf bildirimi aynı sınıf olarak ele alınır, ve
birlestirilir. Yani deneme isimli sınıfın Metot1() ve Metot2() adında iki tane metodu olmus
olur.
Bir türe ait elemanları tek bir dosya içinde toplamak Nesne Yönelimli Programlama
açısından her ne kadar önemli olsada bazen farklı dosyalarla çalısmak kodlarımızın
yönetilebilirliğini artırabilmektedir.
Not : Bu yazı "MSDN Magazine" deki "Future Features of C#" baslıkla bildiri baz alınarak
hazırlanmıstır.

Kaynak Kodunuzu XML ile Süsleyin
Büyük yazılım projelerinde en önemli aktivitelerden birisi proje bazında iyi bir
dökümantasyon yapmaktır; proje analiz sürecindeki dökümantasyon genellikle standart
olan UML diyagramları ile yapılmaktadır, tabi isin bir de gelistiriciye bakan tarafı vardır.
Projenin en nihayi sonu kod yazmak olduğuna göre kodların dökümantasyonu da en az
analiz sürecindeki dökümantasyon kadar önemlidir. Bu yazıda .NET ile birlikte ön plana
çıkan XML yorum formatı ile kodlarımızı nasıl dökümante edebileceğimizi inceleyeceğiz.
Bildiğiniz gibi kodlarımıza yorum satırı koymamızdaki en büyük amaç kodların baskası
tarafından kolaylıkla anlasılabilir hale gelmesini sağlamaktır. Bazen bu islemi kendimiz
içinde yapmak durumunda kalabiliriz, zira bir çok karmasık uygulamada yazdığımız
kaynak koda yıllar sonra belkide aylar sonra baktığımızda vakt-i zamanında neler
düsündüğümüz hemen aklımıza gelmeyebilir. Bu durumda en büyük yardımcımız o
zamanlar tembellik etmeden yazdığımız yorum satırları olacaktır. Eğer kendinize yorum
satırı ekleme alıskanlığını kazandırırsanız bunun getirisini ileride mutlaka göreceksiniz.
Peki ne kadar yorum satırı gereklidir? Bu sorunun cevabı size ve yazdığınız kodların
karmasıklığına göre değisir. Eğer siir gibi kod yazıyorum diyorsanız belkide bir cümlelik
yorum satırı isinizi görebilir, yok arap saçı gibi kod yazıyorum diyorsanız belkide
yazdığınız kod satırı sayısından daha fazla yorum yazmak zorunda kalabilirsiniz.
.NET bir çok meselede olduğu gibi yorum ekleme mekanizmasını da estetik bir sekilde
çözmüstür. Çok değil daha bir kaç yıl öncesine kadar kaynak kodlarımızdaki yorum
satırları // ve /* */karekterleri ile belirtiliyordu. .NET bu eski yorum yazma seklini
desteklemekle birlikte XML formatındaki yorum ekleme mekanizmasıyla ön plana
çıkmaktadır. XML sayesinde artık kodlarımızdaki yorumlar standart hale getirilmistir.
Böylece bir XML yorumunda belirli bir etiketi gördüğümüzde o etiketin içindeki
açıklamanın neyi ifade ettiğini anlarız. Aynı zamanda VS.NET kodlarımızdaki XML
yorumlarını ayrıstırarak saf bir XML dosyası da üretebilmektedir. Bu sayede XML
formatındaki yorum dosyasını istediğimiz sistem ile rahatlıkla entegre edebilirz, söz gelimi
proje yöneticisine XML dosyasındaki yorum bilgilerini HTML formatında sunabiliriz.
XML Yorum Satırları
C#'ta XML yorum satırları " /// (3 adet slash karakteri) " ile baslayan satırlarda yazılır.
Önceden belirlenmis bir takım standart XML etiketleri vardır, öyleki bu etiketler aynı
zamanda ECMA tarafından da standart olarak kabul edilmistir. Ancak XML etiketlerini
programcı istediği sekilde sirketin ihtiyaçlarına yada kendi ihtiyaçlarına göre genisletebilir.
XML yorum yazmadaki belkide tek kısıt XML sözdizimine uyma sartıdır. XML sözdizimine
göre açılan bütün etiketler kapanmalıdır.
En çok kullanılan önceden tanımlı XML etiketleri <param>, <remarks>, <summary>
ve <returns> etiketleridir. Bu etiketlerin bazıları intellisense ve "Object Browser"
programı tarafından kullanılmaktadır. Örneğin VS.NET editöründe bir nesne yaratıldığında
nesne türüne ait yapıcı metottaki parametrelerin kısa açıklaması editör penceresinde
gösterilir. Aynı sekilde kendi yazdığımız sınıflar içinde bu açıklamaların çıkmasını
istiyorsak XML yorum etiketlerini kullanmamız gerekir.
XML yorum etiketleri tür bazında, metot bazında ve parametre bazında olabilir. Tür
bazında yorum ekleme islemi sınıflar, temcilciler, indeksleyiciler, olaylar,
numaralandırmalar(enums) ve yapılar(struct) için uygulanabilir. Metot bazındaki yorumlar
herhangi bir metodun bildiriminden önce yapılır. Parametre bazındaki yorumlar ise bir
metodun parametreleri ve geri dönüs değerleri ile ilgilidir.
Açıklayıcı olması açısından örnek bir sınıf üzerinde XML yorumlarını ve VS.NET gibi akıllı
editörlerde bu yorumların ne gibi etkilerinin olduğunu inceleyelim.
Simdi yeni bir "Class Library" projesi açıp asağıdaki sınıf bildirimini yazın.
using System;
namespace XMLYorum
{
///<summary>
/// Cebir sinifi bazi özel matematiksel islemleri
/// yapmak için çesitli statik metotlar sunar.
///</summary>
public class Cebir
{
/// <remarks>
/// Mutlak Deger Alma Islemi
///</remarks>
///<summary>
/// Parametre olarak gelen sayinin
/// Mutlak Degerini alir.
///</summary>
///<param name="Deger">Mutlak Degeri alinacak sayi.</param>
///<returns>Paremetre olarak gelen sayinin mutlak degeri.</returns>
public static int MutlakDeger(int Deger)
{
if(Deger < 0)
return -Deger;
else
return Deger;
}
/// <remarks>
/// Kare Alma Islemi
///</remarks>
///<summary>
/// Parametre olarak gelen sayinin
/// karesini almak için kullanilir.
///</summary>
///<param name="Deger">Karesi alinacak sayi.</param>
///<returns>Parametre olarak gelen sayinin karesi.</returns>
public static double KareAl(double Deger)
{
return Deger * Deger;
}
}
}
Yukarıdaki örnek koddan görüldüğü üzere sınıf ve metot bildirimlerinden önce /// ile
baslayan satırlarda XML formatında yorumlar yazılmıstır.VS.NET kod editörü ///
karakterinden sonra <summary>, <param name="Deger"> ve <returns> etiketlerini
otomatik olusturdu. <remarks> etiketini ise kendimiz yazmalıyız. XML yorum etiketleri de
intellisense özelliklerinden faydalanır. Otomatik tamamlama islemi etiketler içinde
geçerlidir.
Yukarıdaki örnekte <remarks> etiketi ile sınıf yada metod ile ilgili kısa bir açıklama
yapılır. <summary> etiketi içinde daha ayrıntılı bilgi verilir. Gerekirse çesitli teknik bilgiler
de bu etiket içinde belirtilir.
Simdi C# derleyicisinin XML formatındaki yorumları kaynak koddan ne sekilde ayırdığını
görmek için projeyi derleyelim. Projeyi derlemeden önce eğer VS.NET kulllanıyorsanız
Proje özellikleri(Solutin Explorer'a sağ tıklayarak görebilirsiniz) penceresinden
"Configuration Properties/Build" sekmesinin altındaki "XML Documentation File" kutusuna
olusturulacak XML dosyasının ismini asağıdaki gibi yazmalısınız. Tabi VS.NET editörünün
intellisense özelliklerinden faydalanmak istiyorsak XML dosyasının ismini olusturulacak
DLL ismiyle aynı verilmeliyiz.
Eğer VS.NET gibi akıllı bir editörünüz yoksa .NET Framework ile birlikte ücretsiz olarak
dağıtılan ve komut satırından çalıstırılabilen C# derleyicisini kullanarak ta XML yorum
dosyalarını olusturabilirsiniz. Bunun için yapmanız gereken csc derleyicisini komut
satırından asağıdaki gibi çalıstırmaktır.
csc /t:library /doc:XmlYorum.xml XmlYorum.cs
VS.NET yada C# komut satırı ile olusturulan XML dosyasının yapısı asağıdaki gibidir.
Simdi birde olusturduğumuz Cebir isimli bir sınıfa farklı bir projeden referans verip
metotlarını kullanalım. Yeni bir Console uygulaması açın ve olusturduğumuz Cebir sınıfına
ait assembly dosyasına referans verin. Eğer komut satırı derleyicisi ile çalısıyorsanız "
/r:Cebir.dll " parametresini ekleyin. Tabi bu durumda XML yorumlarının etkisini
göremeyeceksiniz. Çünkü XML yorumlarını göstermek VS.NET teki akıllı editörün bir
yeteneğidir. Notepad'in yada baska text editörlerinden bu tür imkanlar beklememek
lazım!
Cebir sınıfının KareAl() metodunu kullanmak istediğimizde VS.NET'teki kod editörü bize
KareAl() metoduna iliskin XML formatındaki açıklamayı sarı kutucuk içinde asağıdaki gibi
gösterecektir. Böylece kullanacağımız metodun veya sınıfın bildirimine bakmamıza gerek
kalmamıstır.
Aynı durum parametreler içinde geçerlidir. Örneğin KareAl() metodunun çağrım
parantezini yazdığımız anda aktif parametre ile ilgili XML açıklaması asağıdaki gibi
gösterilir.
Aynı XML yorumları VS.NET ile entegre çalısan "Object Browser" arayüzü ile de asağıdaki
gibi gösterilmektedir.
Not : "Object Browser" penceresine erismek için (Ctrl + Alt + J) tus kombinasyonunu
kullanabilirsiniz.
Diğer XML Yorum Etiketleri
XML yorum etiketleri yukarıda anlatılanlar ile sınırlı değildir. Asağıda kullanılabilecek
standart etiketler alfabetik sıraya göre toplu bir sekilde tablo halinde açıklamalarıyla
birlikte verilmistir.
<c> Açıklama içindeki bir bölümün "kod fontu" seklinde olduğunu
vurgulmak için kullanılır.
Örnek :
/// <summary>
/// Bu sınıf <c>Stream</c> sınıfından türemistir.
/// </summary>
public class YeniSınıf
{
...
}
<code> XML açıklaması içinde uzun bir kod bloğu örneği verilecekse diğer
yazılardan ayırmak için kod bloğu bu etiket arasında yazılır.
Örnek :
/// <summary>
/// Bu metod iki Kompleks türeden sayıyı toplar. Örneğin
/// <code>
/// Kompleks sayi1 = new Kompleks(5,6);
/// Kompleks sayi2 = new Kompleks(0,1);
///
/// Kompleks sayi3 = sayi1 + sayi2;
/// </code>
/// </summary>
public Kompleks operator+(Kompleks sayi1, Kompleks sayi2)
{
...
}
<example> Bir metodun yada sınıfın ne sekilde kullanılacağını açıklayan blok bu
etiket içinde yazılır. <code> etiketinin kullanımı ile hemen hemen
esdeğerdedir. <code> etiketini verilen örneğin aynısı <example>
etiketi içinde geçerli olduğu ayrıca bir örnek vermeye gerek yoktur.
<excepiton> Bir metodun fırlatabileceği istisnai durumlarla ilgili bilgi vermek için
kullanılır. <exception> etiketi "cref" niteliği ile birlikte kullanılır.
"cref" niteliği ile fırlatılacak istisnai durum(exception) sınfının türü
belirtilir.
Örnek :
/// <summary>
/// Bu metod iki Kompleks türeden sayıyı toplar. Örneğin
/// </summary>
///
/// <exception cref="IndexOutOfRange">
/// </exception>
/// <exception cref="OzelIstisnaiDurum">
/// </exception>
public Kompleks operator+(Kompleks sayi1, Kompleks sayi2)
{
...
}
<list> HTML kodlarındaki <li> etiketine benzer bir amacı vardır. Liste
seklinde bir yapı olusması gerektiği bildirilir. <listheader> listedeki
baslık bilgisini, <item> listedeki her elemanı, <term> her
elemandaki terimi ve <description> bu eleman hakkındaki detaylı
bilgiyi bildirir.
Örnek :
/// <remarks>Bu bir liste örneğidir.
///
/// <list type="number">
///
/// <listheader>
/// <item>
/// <term>term 1</term>
/// <description>Açıklama 1</description>
/// </item>
/// </listheader>
///
/// <item>
/// <term>term 2</term>
/// <description>Açıklama 2</description>
/// </item>
///
/// <item>
/// <term>term 3</term>
/// <description>Açıklama 3</description>
/// </item>
///
/// </list>
/// </remarks>
public class YeniSınıf
{
...
}
Not : Liste tipi(<list type="number">) "number" olabileceği gibi
"bullet" ve "table" da olabilir.
<para> <summary> gibi uzun açıklama bloklarında bir paragrafı belirtmek
için kullanılır.
Örnek :
/// <summary>
/// Bu sınıf Stream sınıfından türemistir.
/// <para>
/// Bu sınıf aynı zamanda IDisposable arayüzünü uygulamıstır.
/// </para>
/// </summary>
public class YeniSınıf
{
...
}
<param> Bir metodun parametreleri ile ilgili bilgi vermek için kullanılır.
Örnek :
///<param name="Sayi1">Toplanacak birinci sayı</param>
///<param name="Sayi2">Toplanacak ikinci sayı</param>
public static double Topla(int Sayi1, int Sayi2)
{
return Sayi1 + Sayi2;
}
<paramref> <paramref> etiketleri içerisine alınan yerde aslında metodun ilgili
parametresinin olduğu bildirilir. Böylece olusacak XML yorum
dosyasınıdaki bu etiketi farklı bir biçimde yorumlama sansına sahip
oluruz.
Örnek :
/// <summary>
/// Bu sınıfın <paramref name="Sayi1"/> , ve
/// <paramref name="Sayi2"/> ve biçiminde iki parametresi
vardır.
/// </summary>
public static double Topla(int Sayi1, int Sayi2)
{
return Sayi1 + Sayi2;
}
<permission> Üye elemanla ilgili güvenlik bilgisi vermektedir. Örneğin bir metoda
yada sınıfa kimlerin eriseceği ve ne sekilde eriseceği bu etiket
kullanılarak belirtilebilir.
Örnek :
///<permission cref="Private">Herkes bu metoda erisebilir.
///</permission>
public static double Topla(int Sayi1, int Sayi2)
{
return Sayi1 + Sayi2;
}
<remarks> Bir tür hakkında kısa bir açıklama vermek için kullanılır.
Örnek :
///<remarks>
/// Özel cebirsel islemleri tanımlar.
///</remarks>
public class Cebir
{
....
}
<returns> Bir metodun geri dönüs değeri ilgili bilgi vermek için kullanılır.
Örnek :
/// <remarks>
/// Kare Alma Islemi
///</remarks>
///<summary>
/// Parametre olarak gelen sayinin
/// karesini almak için kullanilir.
///</summary>
///<param name="Deger">Karesi alinacak sayi.</param>
///<returns>Parametre olarak gelen sayinin karesi.</returns>
public static double KareAl(double Deger)
{
return Deger * Deger;
}
<see> Yazı içinde bir bağlantının(link) olacağını belirtir. "cref" niteliği ile
birlikte kullanılır. "cref" niteliği bağlantının olacağı üye elemanı
simgeler.
Örnek :
/// <summary>
/// Bu metod iki Kompleks türeden sayıyı toplar. Örneğin
/// </summary>
///
/// <see cref="KompleksAlgoritmalar"/>
public Kompleks operator+(Kompleks sayi1, Kompleks sayi2)
{
...
}
<seealso> Yazı içinde ilgili elemanla yakından iliskili olan diğer elemanlara
bağlantı vermek için kullanılır. Kullanımı <see> etiketi ile aynıdır.
Örnek :
/// <summary>
/// Bu metod iki Kompleks türeden sayıyı toplar. Örneğin
/// </summary>
///
/// <seealso cref="operator-"/>
/// <seealso cref="operator/"/>
/// <seealso cref="operator*"/>
public Kompleks operator+(Kompleks sayi1, Kompleks sayi2)
{
...
}
<summary> Üye elemanla ilgili genis açıklama yazmak için kullanılan bir
etikettir.
Örnek :
///<summary>
/// Cebir sinifi bazi özel matematiksel islemleri
/// yapmak için çesitli statik metotlar sunar.
///</summary>
public class Cebir
{
...
}
<value> Sınıfın bir üye elemanı olan özellikler (Property) hakkında bilgi
vermek için kullanılır.
Örnek :
///<value>
/// Kontrolün rengini belirtir.
///</value>
public int Renk
{
get { return a;}
set { a = value;}
}
Kodlarınızı XML yorumları ile süslemiyi unutmayın !...
Visual C# ile Windows Kontrolü Hazırlama
Simdi sizlere Visual C# NET’te bir Windows Control nasıl yapılır ve bu Windows Control’ü
programlarımızda nasıl kullanırız onu göstereceğim. Göstereceğim örneği çok basit
seçtim, bunun nedeni de yaratıcılığı siz arkadaslarıma bırakmayı uygun görmemdir.
Simdi örneğimizi adım adım inceleyelim.
I. Visual Studio .NET’te yeni bir proje açalım ve Windows Control Library’yi
seçelim ve adını değistirelim(Ben burada NewControls adını verdim siz istediğiniz
adı verebilirsiniz.)
II. Daha sonra Anlamlı bir isim olması için UserControl1’in adını MyTextBox
olarak değistirelim.
III. Bu değisiklikleri yaptıktan sonra MyTextBox’ın üzerine bir TextBox
yerlestirelim ve onun adını da değistirelim. Ben burada adını myTBox olarak
değistirdim.
IV. Evet simdi MyTextBox’ın kodunu açalım ve bir TextBox’ın yapması gerektiğini
düsündüğümüz özellikleri de eklemek için istediğimiz metodu buraya yazalım.
Burada örnek olarak myTBox üzerindeki bilginin integer olup olmadığını control
eden bir metod yazalım ve metodun dönüs değeri eğer integer değilse 0 (sıfır)
olsun. Eğer dönüs değeri 1 olursa integer olsun.
V. Kod yazımını tamamladıktan sonra derleyin, eğer derlemek yerine direk
çalıstırırsanız(run) asagıdaki uyarıyı alırsınız(Kısaca verdiği uyarı : "Bu Windows
Control tek basına çalısamaz. Bunu baska projelerde kullanmalısınız.") Ama sorun
değil çünkü yaptıgımız Windows control’ü zaten diğer projelerde kullanmak üzere
tasarladık.
Simdi sorabilirsiniz bu Windows Control’ü projelerimizde nasıl kullanacağız? Yine adım
adım anlatalım.
I. Yeni bir proje açın veya önceden var olan bir projeyi açın. Ben burada
Deneme adında yeni bir proje açtım.
II. Daha sonra .Net’in Ana Proje Penceresindeki MenuBar’dan Tools’I tıklayın
açılan menuItem’lardan Add/Remove ToolBoxItems’ı tıklayın.
III. Daha sonra açılan pencereden .NET Framework Components’in
seçili olmasına dikkat edin. Eğer seçili değilse onu seçin. Ve Browse diyerek
daha önce kaydetmis oldugumuz NewControls projesinin içine girilelim oradan
bin’e oradan da Debug’ın içine girelim. Daha sonra karsımıza çıkan
NewControls.dll adlı dosyayı seçip OK tusuna basalım. Daha sonra tekrar
NET Framework Components’in seçili oldugu pencerede OK tusuna basalım.
IV. Simdi ToolBox’a bakalım. Đste karsımızda MyTextBox’ımızın yazılı
olduğu bir ToolBox’ımız oldu. Artık MyTextBox’ımızı diğer Tool’ları
kullandığımız gibi kullanabiliriz.
Artık bundan sonrasını siz uygulama gelistirici arkadaslarıma bırakıyorum. Unutmadan;
artık bu hazırlamıs oldugumuz Tool’u diger projelerimizde de kullanabiliriz. Anlasılmayan
herhangi birsey olursa aytacozay@msakademik.net adresine mail atabilirsiniz.
Not: Eğer kendi Tool’larınızı kullanarak bir proje yapıyorsanız ve yaptıgınız projeyi baska
makinelerde çalıstırmak isterseniz kullandıgınız Tool’ları o makineye yukarıda bahsettigim
sekilde yüklemeniz gerekir.

C#’ta Inheritance(Miras) Kavramı
Bu yazıda inheritance’ın programlamada ne anlama geldiğinden bahsedeceğim.
Inheritance aslında Oject Oriented Programming!in (Nesne Yönelimli Programlama) üç
prensibinden bir tanesidir. Diğer iki prensip ise encapsulation ve polymorphism’dir.
Tabii ki diğer iki prensibe bu yazıda değinmeyeceğim. En sade sekliyle: inheritance
sayesinde bir sınıfın metodlarını kullanan baska sınıflar türetilebilmesine yarar
diyebiliriz. Ancak ayrıntılarına birazdan ineceğim. Eğer daha önce nesne tabanlı bir
programlama dili kullandıysanız, (Java ve C++ gibi) C#’ta inheritance’a çok çabuk
adapte olursunuz. Aslında su ana kadar bahsettiklerim genel kültürden ibaretti ve
eminim çoğunuz da bunları biliyordunuz. (Nesne Tabanlı Programlama geçmisi
olmayanları da düsünerek böyle bir giris yaptım.)
Evet simdi ana kısma yani programın nasıl yazılacağına geliyoruz. Bunun için basit bir
örnek vereceğim. Düsünün ki student adında bir sınıfımız(class) olsun. Ayrıca bir de
teacher adında bir sınıfıımız olsun. Bunların ortak özellikleri nedir? Tabii ki insan
olmaları diyeceksiniz ve ana sınıfımıza yani person sınıfına ulasmıs olacaksınız. Simdi
basitçe özetlersek person sınıf’ından teacher ve student adında iki sınıf türetmis olduk.
Sırada bunun kodunu nasıl yazacağımız var. Alıskanlıklara devam edip adım adım kodu
yazalım. (Bunu program yazarken de ilke edinirseniz faydalı olacağına inanıyorum.
Önce ne yapacağınızı adım adım belirleyin sonra belirlediklerinizi adım adım
uygulamaya geçirin.)
I. Đlk önce person sınıfını yazalım.
using System;
using System.Windows.Forms;
namespace Miras
{
public abstract class Person
//sınıfın sadece türetileceğini
//belirtmek için sınıfı abstaract keyword’ünü kullanarak soyutladık
//Ancak burada abstaract keyword’ünün kullanılmasındaki temel
//faktör bu sınıfın abstract metod içermesidir.
{
//Türetilen sınıflarda kullanılmak üzere 3 tane değisken tanımladık.
protected string Name;
protected int Age;
protected string Gender;
//Türetilen sınıflarda metodun içi doldurulması için
//abstract olarak makeAction metodu tanımladık
public abstract void makeAction();
public Person()
{
}
}
}
II. Simdi de Student sınıfını yazalım.
using System;
using System.Windows.Forms;
namespace Miras
{
//Student class'ı Person class'ından miras aldığını belirtiyoruz.
public class Student:Person
{
//Person sınıfında tanımlanan abstract metodu override ederek
//metodun içini istediğimiz gibi doldurduk.
public override void makeAction()
{
MessageBox.Show("Ben bir öğrenciyim");
}
public Student(string name,int age,string gender)
{
this.Name=name;
this.Age = age;
this.Gender=gender;
}
}
}
III. Sıra Teacher sınıfını yazmaya geldi.
using System;
using System.Windows.Forms;
namespace Miras
{
public class Teacher:Person //Teacher class'ı Person class'ından
// miras alıyor
{
private string Unvan; //Teacher sınıfında kullanılmak üzere
//Unvan adında bir değisken tanımladık.
//Person sınıfında tanımlanan abstract metodu override ederek
//metodun içini istediğimiz gibi doldurduk.
public override void makeAction()
{
MessageBox.Show("Ben bir öğretmenim");
}
public Teacher(string name,int age,string gender,string unvan)
{
this.Name=name;
this.Age = age;
this.Gender=gender;
this.Unvan=unvan;
}
}// teacher sınıfının sonu
}// Miras isim uzayının sonu
Simdi dikkat edilmesi gereken noktaları sıralayalım:
I. Abstaract (soyut) sınıftan yeni bir sınıf türetilemez. Örneğimizde person sınıfı abstract sınıftır ve new
anahtar sözcüğü kullanılarak yeni nesne olusturulmaz.
II. Abstract metodların mutlaka içleri bos olarak yaratılır ve türetildikleri sınıflarda (en az bir sınıf
türetilmek zorunda) mutlaka içleri override anahtar sözcüğü kullanılarak doldurulur.
III. Bir sınıftan miras yolu ile baska bir sınıf türetileceği zaman temel sınıf(base class) illa ki abstract
anahtar sözcüğü ile tanımlanmak zorunda değildir. (Eğer abstract metod içermiyorsa.)
Not: Yukarıda kodlarını yazdığım sınıfların basit bir kullanımını içeren Miras adındaki
projeyi indirmek için buraya tıklayabilirsiniz. Bu sayede kafalarda hiçbir soru isareti
kalmasın istiyorum. Projeyi çalıstırdığınız zaman bir windows form’u gelecek. Bu formun
üzerinde iki tane buton var.
Student Action butonunu tıklarsanız Student sınıfının içindeki makeAction metodu
çalısır. Daha sonra da windows formu yaratıldıktan hemen sonra yaratılan student
nesnesinin Name, Age, Gender değiskenleri bir mesaj kutusu aracılığıyla görüntülenir.
Teacher Action butonunu tıklarsanız Teacher sınıfının içindeki makeAction metodu
çalısır. Daha sonra da windows formu yaratıldıktan hemen sonra yaratılan teacher
nesnesinin Unvan, Name, Age, Gender değiskenleri bir mesaj kutusu aracılığıyla
görüntülenir.
Kafanıza takılan sorular için mail adresim aytacozay@msakademik.net
C#'a Kısa Bir Giris
Ben C’ dilini öğrenmeye 1 yıl önce Üniversite’de MS DOS ortamında yaptığımız basit
matematiksel islemlerle basladım.Gerçektende her programda alısıla gelmemis bir çok
komut vardı. Günümüzde kullanılan C# diline göre çok gelismemis olan bu dile o kadar
ısınmıstık ki artık uygulamalara yetisemez olduk. C çok eskiden çıkan bir dil fakat gelisimi
ve insanların ona yetismesi çok hızlı idi. Sırasıyla C,C++,C# ben buna 3D diyorum yani 3
dev demekle yetiniyorum. C dili bir çok dilin temeli veya üstünde bir dil. Hemen hemen
her alanda kullanılmaktadır. Mesela Javascript,ActionScript... En önemli olan uygulama
alanı ise Windows ve Linux gibi güçlü bir isletim sistemlerinin C de yazılmasıdır. C dilinin
uygulama alanları sadece saydıklarımla sınırlı değildir ama bu alanların hepsini burda
listelemem mümkün değildir.
.NET Framework, programcılara asina olduğu kod dilini kullanma özgürlüğü tanıyarak bir
devrim gerçeklestirdi. Ve, belli belirtimlere sadık kalındığı sürece, farklı dillerle yazılmıs
uygulamaların birbiriyle etkilesebileceğinin de teminatını verdi.
Evet, .NET diller arası etkilesime olanak tanıyan, bir çok dile destek veren bir platform.
Üçüncü parti derleyiciler yazılarak .NET için her an yeni bir dil daha yazılabilir. Ama
herseyden önce, .NET'in beraberinde sunduğu dillere bakmak gerekiyor. Bu diller temel
olarak 4 tane: C++, Visual Basic .NET, C# ve J#.NET. Dikkat edilirse bu listede, "ben
yeniyim" diye göz kırpan bir tanesi var : C#. Yazımızda, bu yeni dili tanımaya çalısacağız.
C# (si sarp) herkesin dile getirmis olduğu gibi C++ ve Java ‘nın birlesmesiyle
olusmustur. Henüz nasıl bir birlesme sekli olduğuna dair tam bir fikrim yok ama C#
mükemmel bir kütüphaneye sahip. Bu kütüphaneye ufak bir göz asinalığımız olacak ama
ilerideki yazılarımızda diğer dillerden büyük bir farkı olan esnek bir yapıya sahip olmasını
inceleyeceğim. Nedir bu esneklik? Yani Program yazarken "of be bu dilin de bu özelliği
yokmus" dediğimiz anlar olmustur. C ile de süphesiz nesnel programlama yapabiliriz.
Fakat bunu yapabilmek oldukça zordur. C++ ise Nesne yönelimli programlamaya imkan
vermekten öte zaten bu paradigmaya göre tasarlanmıstır ve yapısındaki araçlar
sayesinde bunu kolaylastırmıstır. Đste C- C++ arasındaki fark bu peki C#'ın özelliği nedir?
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) yaklası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ıs 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 ilisikili verilerlerdir. Diğer taraftan sınıfın bizim için değisik isleri
yapmasını metodları vasıtasıyla sağlarız. Sınıf tasarımı çok önemli bir is olup; deneyim,
konsantrasyon ve dikkat ister. Sınıfımızın özelliklerini tutan veriler, program akısı
sırasında sınıf dısında değistirilebilir veya bu değerlere ulasmak istenilebilir.
Elbetteki C# hakkında bilinmesi gerekenler bu kadarla sınırlı değildir. Bundan sonraki
yazılarımda herseyi daha ayrıntılarıyla aktarmaya çalısacağım.
.NET Đçin Tavsiye Edilen Đsimlendirme Konvansiyonları – 1
Merhaba, bu makalemizde artık programcılık hayatımızın heryerinde, küçüklü
büyüklü her program için ihtiyaçtan çok bir zorunluluk haline gelen isimlendirme
tekniklerine, tarihçelerine değinecek,kendi isimlendirme stilimizi nasıl olusturabiliriz ona
bakacağız. 1. bölümün tamamını ,yani bu yazıyı tümüyle bu alacakken , 2. bölümününde
özellikle Microsoft’un .Net için de tavsiye ettiği konvansiyon olan Pascal & Capitalized
Form (Pascal ve Büyük harfler notasyonu) ve uygulaması üzerinde duracağız.
Neden Đsimlendirme Konvansiyonlarını Bilmeliyiz?
Tabii ki bu konvansyonları kullanmak zorunda değiliz,kendi konvansiyonumuzu
olusturup kodlamaya da geçebileceğimiz gibi,konvansiyonsuz da kodlama yapabiliriz.
Fakat ileri düzey programlamada isimlendirmenin birçok avantajı vardır.
Đsimlendirme kavramı,programlama dünyasında komplex kodların yazılmaya
baslanmasıyla,özellikle de OOPL (Nesne Yönelimli Programlama Dilleri) nin gelismesiyle
büyük önem kazandı. Çünkü ortak olmayan ve anlamsız isimler,modullere bölünmüs ve
çözüm uzaylarına ayrılmıs,spesifikasyonları hazırlanmıs,yani en önemli bölümü halledilmis
bir programın sadece kodlama asamasında çesitli ciddi hatalara yol açılmasına sebep
oluyordu. Bir kisiden fazlasının çalısmasını gerektiren projelerde insanlar birbirlerinin
yazdıkları kodu anlamıyor, hatta bir kisinin kendi yazdığı programı bile daha sonra
baktığında anlaması güçlesiyordu.
Đsimlendirme konvansiyonlarını kullanmanın diğer bazı avantajları ise sunlardır :
o Programa vereceğimiz isimler anlamlı olur.
o Hepsi bir kurala bağlı olduğu için düzenli görünür.
o Đsim seçme islemi artık mekanik olduğundan üzerinde düsünmeye gerek
kalmaz, hızlı çalısırsınız.
o Takım çalısmalarında aynı dili konusmanızı sağlar.
o Kodlarınız anlasılır olacağından daha az yorum yazabilirsiniz.
o Kodunuzu böceklerden(bugs) arındırırken faydası olur.
o Kod standardize olduğu için daha sonra programınızın kodunu baska bir
program yardımıyla iyilestirebilirsiniz.
o Ortam hazırlayıcıları tarafından belirlenen notasyonu kullanmak,ortam
tarafından otomatik olarak koda yerlestirlien kod parçaları ile de uyumlu
olacağı için ( ör: Form Designer’ın koda eklentileri ) tam uyum sağlar.
Đsimlendirme konvansiyon Çesitleri
Bu sorunlara bir çözüm bulmak için notasyon adı verilen standartlar gelistirildi.
Ortamların farklılığından dolayı birçok standard ortaya çıktı. Bunlardan bazıları sunlardır :
Hungarian notation (Macar notasyonu):
Macar notasyonu diye bilinen bu notasyon diğer notasyonların atası olarak kabul
edilmesi itibariyle,günümüzde geçerliliği azalmıstır.
DOS’un ilk çıktığı zamanlarda Microsoft’un sef direktörü Charles SIMONYI
tarafından gelistirilen bu tanımlayıcı isimlendirme notasyonunun temelinde,ismin önüne
tipini yazarak aktif isimlendirmeyi sağlamaktır. Örnek verecek olursak, bir boolean flag
için “bFlag” isimlendirmesi uygun bir isimlendirme seklidir. String olarak
strFirstName,integer olarak iNumberOfDays uygun isimlendirmelerdir.
Bu isimlendirmenin getirdiği faydalar artık modern programlama ortamlarının
gelistirilmesiyle ortadan kalkmıstır. Çünkü,mesela .Net gibi bir ortamda bir değiskenin tipi
zaten kodun her yerinde bellidir,bundan dolayı ismi uzatmaya gerek yoktur. Yani,bu
notasyonun günümüzde kullanımı artık azalmıstır.
Ayrıca ortamların desteklediği tür sayısı günden güne arttığından bu tür bir
isimlendirmeye gitmenin bayağı bir güç olacağı açıktır. Bu türün Extended Hungarian
Notation,Modified Hungarian Notation ,Simple Hungarian Notation Hungarian Notation
türleri bulunmaktadır.
MFC naming (Member-First Case) (Đlk harfi tanımlayıcılı notasyon):
Bu notasyonun temelinde tanımlayıcının tipinden çok türü önemlidir,yani int
mi,short mu olmasından çok üye,sınıf,fonksiyon olmasına gore isimlendirilir. Event
isimleri ise (On) ile baslar. Örnek olarak m_socket, i_counter,OnClose bu notasyona göre
iyi isimlendirilmis tanımlayıcılardandır.
Bu isimlendirme tekniğinin ise eskidiği Macar notasyonunda belirttiğimiz
nedenlerden ötürü açıktır.
GNU Notation (GNU Derleyici Notasyonu)
Üstte belittiğimiz diğer notasyonlardan farklı olarak bu notasyonda kelimeler
arasında altçizgi ( _ ) karakteri bulunma sartı getirilmistir. Örneğin
global_number_increase güzel bir isimlendirme iken icantreadthis iyi değildir. Ayrıca bazı
GNU derleyicilerinde 8 ve/veya 14 harften fazlasına izin verilmediğinden zorunlu olarak
bu derleyicilerin standartlarına harf sınırlaması da getirilmistir.
Ayrıca yine bazı derleyicilerde ( __ ) ile baslayan değerler ayrılmıstır. Bundan
dolayı altçizgi ile baslayan isimlendirmeler iyi isimlendirme örneği değildirler.
Diğer bazı notasyonlar ise Sun – Java notation, SmallTalk – Roll Based Naming,
Taligent Form dur.
Kendi Đsimlendirme Konvansiyonumuzu Olusturma
Yazının bu kısmına kadar , varolan isimlendirme çesitlerini iyice anladığımızı
umuyorum. Fakat hala benim kendi isimlendirme standardım olmalı diyorsak, dikkat
etmemiz gereken bazı noktalar var.
Bir isimlendirme konvansiyonu olustururken,dikkat etmemiz gerekenlerden ilki,
isimlerin anlasılabilecek kadar uzun, fakat yazılabilecek kadar kısa olmasıdır. Bunları
olustururken konvansiyonları kullanmanın temel faydalarına zarar vermemeyi
gözetmeliyiz.
Đsimlendirme konvansiyonumuzu seçerken ortam,dil, ve kültür özelliklerine,
isimlendirme konvansiyonu mantığının temelinde yatan estetik kaygıya ve algoritma
olusturmanın temel sartlarına (verimlilik,isteneni verme vs vs …) dikkat etmemiz gerekir.
Tüm Đsimlendirme Konvansiyonlarınında Bulunması Gereken Temel
Özellikler
Bütün standartlarda ortak olması gereken noktaları ise söyle sıralayabiliriz :
o Tanımlayıcının(değiskenin,sınıfın,metodun vb...) amacı doğrultusunda isimler
verilmesi gerekir. Mesela okuldaki öğrenci sayısını tutan bir değiskene “tamsayi”
seklinde isim vermek yerine “ogrencisayisi” seklinde isim vermek daha mantıklı
olacaktır.
o Tanımlayıcının ismi büyük ve küçük harfleriyle okunabilir ve anlasılır uzunlukta
olmalıdır.
o Mümkün olduğunca kısaltmaları azaltmalıdır. Çünkü kısaltmalar çoğu zaman
tehlikeli olabilmektedir. Örneğin “Ctr” “Control” olarak anlasılabileceği gibi
“counter” olarak da anlasılabilir.
o Tanımlayıcıların isimleri,diğer tanımlayıcılar arasında ayırt edici özellik olarak
kullanılmamalıdır. Örneğin “Counter” ve “counter” adında iki değiskenimiz
olmamalıdır
Daha sonra is kendi isimlendirme standardımızın sartlarını olusturmaya bakıyor.
Bunun için bu yazı genel bir fikir verebilir. Đsimlendirme tekniklerinizi,hiçbir
tanımlayıcı tipi açıkta kalmayacak sekilde tasarladıktan sonra projenin daha sonra da
aynı mantıkla gelistirilmesi ve isimlendirme konvansiyonunuzun kalıcılığını
koruyabilmesi için iyi bir sekilde dökümante etmelisiniz.
Dökümantasyonunuz isimlendirme konvansiyonunuzla ilgili herseyi içermelidir(Tip
isimleri,ön ekler,arka ekler,kısaltmalar,eklentiler,özel karakterler,vb...) .
Ve son söz olarak , unutmayalım ki , bir çok kod bir kez yazılır ama binlerce kez
okunur. Bunu göz önüne alarak kodlamamızı daha profosyonel standartlara tasıyalım.
Yazının 2. bölümü Microsoft’un .NET ortamı için önerdiği formlar olan PASCAL &
CAPITALIZED FORM , CAMEL FORM adlı notasyonları derinlemesine inceleyecek , ve
herhangi bir isimlendirme sistemimize aykırı davranıldığını çok büyük kod
parçalarında nasıl anlayacağımızı anlatacağım.
Görüsmek üzere,
C# 'ta Kapsülleme, Erisim Belirteçleri ve Polymorphism
Geçen yazımda inheritance' tan bahsederken encapsulation diye bir kavramdan
bahsetmistik, simdi bu kavramı açıklayacağım. Daha önceki yazımda belirttiğim gibi 3
OOP prensibinden biri. Türkçe karsılığına gelirsek kapsülleme demek. Ancak bilgisayar
terimi olarak biraz açarsak kapsülleme, yönettiği kod ve veriyi birbirine bağlayan ve bu
ikisini dıs kaynaklı karıstırma ve yanlıs kullanımdan koruyan bir mekanızmadır. Bu sayede
veriyi dıs ortamdan koruyan bir ambalaj vazifesi gördüğünü de söyleyebiliriz. Simdi
kapsüllemeyi biliyoruz da C#'ta yada .Net Framework'ünde ne gibi farklılıklar var
diyeceksiniz? Öncelikle .Net Framework'ünde gelen yeniliklerden bahsedelim.
I. Erisim belirteçlerinin(Access Modifiers) varlığı kapsüllemeyi çok daha rahat
yapabilmemize olanak sağlar. Bu sayede bir metod veya bir değisken anahtar
sözcükler(keywords) aracılığıyla sadece önceden belirlenen sınırlar dahilinde kullanılabilir.
Yada bunlara belirlenen sınırlar içinden ulasılabilir. Burada bahsedilen keyword'leri
birazdan açıklayacağım. (Tabii ki C#'ta kullanılan keywordleri açıklayacağım. Ve
kullanımlarını basitçe anlatacağım.)
II. Özellik(Property) Sahalarının kullanımı (Bunun yapımını ilerde C# kodu ile
göstereceğim.) Bu sayede .Net Framework kapsüllemeyi destekler.
III. Soyut sınıf(abstract class) ve soyut metodların(abstract methods) kullanımı. Aslında
kalıtım(inheritance) konusunu anlatırken taban sınıfımız(base class) soyut sınıf idi. Onun
için bu kısmı sadece açıklayacağım. Örnek vermeyeceğim. Örneği görmek isteyenler
miras(inheritance) konusunu anlattığım yazıdaki örneği incelerlerse istedikleri bilgiye
ulasabilirler.
Evet bu kadarlık giris yeter. Simdi yukarıda anlattığım 3 maddeyi enine boyuna
tartısalım.
I. Erisim belirteçlerinin ne ise yaradıklarından yukarıda bahsettiğim için burada direkt
erisim belirteçlerinin neler olduklarını yazalım ve erisim sınırlarını çizelim. Erisim sınırları
genis olandan dar olana doğru bir sıralama yaparsak.
• public: Bütün her yerden erisilmek istenen veriler public anahtar sözcüğü ile
birlikte kullanılır. Sadece aynı proje içerisinden değil diğer projeler içerisinden de
erisilebilir.
• internal: Aynı assembly içindeki tüm sınıflar erisebilir. Yani public anahtar
sözcüğünün sadece aynı proje içinden erisilebileni. (VB .Net'te ise Friend anahtar
sözcüğüne karsılık gelir.)
• protected: Protected anahtar sözcüğü ile birlikte kullanılan verilere ise sadece bir
alt sınıfa kadar erisilebilir.
• private: Bu ise sadece tanımlandığı sınıfta geçerli olan veriler için kullanılır.
Ancak kontrollerde(controller) yaygın olan kullanım sekli kontrollerin dısarıdan erisilmesi
istenen metodlarının(aynı anda diyelim ki 3 tane kontrol'ün belli metodlarının çalısması
gerekli olabilir.) public anahtar sözcüğü kullanılan bir metod içinde tanımlanmasıdır.
Simdi bu durumun nasıl yapıldığını gösteren mini bir örnek kod yazalım. Kodda belirtilen
kontrollerin daha önceden tanımlanmıs olduğunu düsünelim.
public void ChangeColor(Color color)
{
this.groupBoxLine.BackColor = color;
this.groupBoxOutCity.BackColor = color;
this.groupBoxExternalPriceDetails.BackColor = color;
this.groupBoxInternalPriceDetails.BackColor = color;
this.groupBoxUser.BackColor = color;
this.groupBox1.BackColor = color;
this.btnAddNewLine.BackColor = color;
this.btnAddNewUser.BackColor = color;
this.btnCentralReport.BackColor = color;
this.btnChangePassword.BackColor = color;
this.btnDeleteExternalLine.BackColor = color;
this.btnDeleteInternalLine.BackColor = color;
this.btnDeleteUser.BackColor = color;
this.btnExit.BackColor = color;
}
Yukarıdaki metod bir renk parametresi gönderilerek çağrıldığı zaman yukarıda yazan
bütün (daha öncede private anahtar sözcüğü ile tanımlanmıs olduklarını kabul etmistik.)
kontrollerin rengini gönderilen renge değistirmeye yarıyor. Bu sayede yukarıdaki
kontrollerin hepsinin BackColor dısındaki metodları dıs dünyadan soyutlanmıs oluyor.
Aslında yaptığımız metod public anahtar sözcüğü ile tanımlanmayıp internal anahtar
sözcüğü ile de tanımlanabilir. Bu bizim metodun içindeki kontrollere ait BackColor
metodlarının dıs dünyadan ne kadar soyutlanmasını istediğimiza bağlıdır.
II. Özellik sahaları sınıflara ait özel(private) değiskenlerin aynı metodlar gibi dıs dünyaya
açılmalarını sağlıyor. Sadece okuma amaçlı dısa açılım yapılabildiği gibi hem okuma-hem
yazma amaçlı bir açılım da yapılabilir. Teorik olarak sadece yazma amaçlı da bir açılım
olsa da ne kadar mantıklı olur bilmem!!!! Simdi örneklerimize geçelim.
private int currentExNumber = -1;
private int loginStatus = 0;
public int CurrentExNumber //Sadece okuma amaçlı özellik
{
get
{
return currentExNumber;
}
}
public int LoginStatus //Hem okuma hem yazma amaçlı özellik
{
get
{
return loginStatus;
}
set
{
loginStatus = value;
}
}
Simdi yukarıdaki özellikleri kullanırken nesneadi.LoginStatus ve
nesneadi.CurrentExNumber seklinde kullanabiliriz. Yalnız dikkat etmemiz gereken
CurrentExNumber kullanılacağı zaman sadece esit isaretinin(=) sol tarafında
kullanılabilecek olması. Çünkü basta da belirttiğimiz gibi sadece okuma yapabildiğimiz için
get metodu var. Zaten bir değer atamaya kalkarsak hata verecektir.(Derleme esnasında
özelliğin sadece okuma amaçlı olduğuna dair debug penceresinden mesaj verir.) Bu
sayede de değistirilmesini istemediğimiz ama kullanmak zorunda olduğumuz verilerin dıs
ortamdan hem soyutlanmasına hem de bunların dıs ortama belirli izinler dahilinde
açılımına izin vermis olduk.
III. Aslında soyut sınıf ve soyut metod'dan daha önce az da olsa miras konusunu
anlatırken bahsetmistim. Ancak simdi biraz polymorphism'den bahsederek bu kavramları
biraz daha açacağım. Polymorphism kapsülleme ve miras'dan ayrı düsünülemez.
Polymorphism Yunancada "çok formluluk" anlamına gelmektedir. Polymorphism ile soyut
sınıf arasındaki ilskiden bahsetmeden önce soyut sınıf ve soyut metodlarla ilgili bir iki
ayrıntı daha verelim. Soyut sınıf sadece taban sınıflarında kullanılır ve yeni nesne
yaratılmasında kesinlikle kullanılamaz. (Yani new anahtar sözcüğü kullanılamaz.)
Soyut metodlara gelince bunların ise soyut sınıflarda kullanılacağından bahsetmistik.
Bunun bize sağladığı avantaj bu metodların türetilen sınıflarda nasıl gerçeklestirildiğini
bilmek zorunda olmamamızdır. Aslında bunu söyleyerek polymorphism'in yararından
bahsetmis olduk. Yani polymorphism veri soyutlaması yaparak sadece ilgilenmemiz
gereken olaylar ve verilerle ilgilenmemize olanak sağlıyor. Bu sayede taban sınıfından
türetilen ve aynı metodu farklı gerçeklestirimlerle(implementation) kullanan birden fazla
sınıfa sahip olabiliyoruz. En basit örnek üçgen bir çokgen, kare de bir çokgen ve her
ikisinin de bir alanı mevcut. Hemen basitçe bir taslak çıkarırsak çokgen sınıfı soyut taban
sınıfı ve alan adında soyut bir metoda sahip. Üçgen ve kare sınıfları ise türetilen sınıflar
ve alan metodunu istedikleri biçimde gerçeklestiriyorlar. (Bu islemlerin nasıl yapıldığı
miras konusunu anlattığım yazıda mevcuttur.)
Bir de soyut özellikler(abstract property) var. Bunların kullanımı ise soyut metodlar ile
özelliklerin birlikte kullanımı ile ortaya çıkmakta. Buna bir örnek kod verirsem anlasılması
daha kolay olacaktır. Ancak bunların kullanımına çok sık rastlamadığımı belirtmem
gerekir.
Sanırım asağıdaki örnek kod parçası soyut özelliklerin kullanımını daha da netlestirmistir.
abstract class Taban // Soyut sınıf
{
protected int CurrentExNumber = -1;
public abstract int GetCurrentExNumber// Soyut özellik
{
get;
}
}
class Turet: Taban //Turet adlı bir sınıf türetiliyor
{
public override int GetCurrentExNumber// overriding property
{
get
{
return CurrentExNumber+1;
}
}
}
Polymorphism'den bahsettik. Simdi ise yalancı polymorphism'den bahsedelim. Aslında bir
örnekle biraz daha açarsam daha net olur. Diyelim ki bir karsılastırma metodunuz var ve
hem integer hem de string veri tiplerini karsılastırmak istiyorsunuz. Yalancı polymorphism
sayesinde aynı isimde iki metod yazarak bu isteğinizi gerçeklestirebilirsiniz. Bunun için
mini bir örnek kod yazalım isterseniz.
Asağıda yazacağım metodların aynı sınıf içinde yazıldığını düsünelim. Simdi bu metodları
kullanırken metodların içinde yer aldığı sınıftan üretilen nesneninadi.karsilastir(
yazdığımız anda kod tamamlayıcısı bize iki seçenek sunar biri bu metodun iki tane integer
veri tipi ile çalıstığı, ikincisi ise bu metodun iki tane string veri tipi ile çalıstığıdır. Bu
sayede bir arabirim ile birden fazla metod gerçeklestirilmis olur.
Aslında bir metodun birden fazla gerçeklestirime sahip olması olayına overloading denir.
Dikkat edilmesi gereken nokta overloading ile overriding'in birbirine karıstırılmamasıdır.
Unutmayın overloading'te bütün islemler aynı sınıf içerisinde oluyor. Overriding'te ise tek
bir sınıf yerine taban sınıfı ile bu sınıftan türetilen sınıflar isin içine giriyor.
public void karsilastir(int sayi1, int sayi2)
{
//Metodun iç implementasyonunu sizlere bıraktım.
}
public void karsilastir(string data1, string data2)
{
Metodun iç implementasyonunu sizlere bıraktım.
}
Evet bu yazıda anlatacaklarım sona erdi. Kafanıza takılan kısımlar için mail adresimi
tekrarlıyorum
Yeni Nesil Đs Uygulamalarının Mimarı C# ve Diğer Diller
Sirket yöneticileri gelistirilecek proje için bir programlama dilini seçmek zorunda
kaldığında genelikle su soruyu sorar : Hangi programlama dili ile projeyi en etkin ve en
hızlı sekilde müsterime sunabileceğim hale getirebilirim? Bu sorunun çözümüne ulasmak
o kadar da kolay olmuyor maalesef. Çözüme zor ulasmada programlama dillerinin fazla
olmasının etkisi omakla beraber her bir programlama dilinin sunduğu standart
kütüphanenin farklı olmasının da etkisi oldukça fazladır. Özellikle günümüz is
uygulamaları birden fazla platformu destelemek zorunda kalmıstır. Buda seçilecek
uygulama gelistirme ortamının önemini açıkca göstermektedir. Uygulamaların internet
ortamına tasınması ile birlikte bir programlama dilinden beklenen özelliklerde doğal
olarak değismistir. 1970’ li yıllarda bir mikroislemciyi programlamak ne denli önemli
olduysa 2000’li yıllarda interneti programlamak o kadar önemli olmustur.
Đnternet’in is dünyasına girisi ile birlikte gelistirilen uygulamalardan beklenenler de
değismistir. Bu durum doğal olarak uygulama gelistiricileri doğrudan etkilemistir. Đnternet
ortamında çalısan ve dağıtık yapıda çalısabilen çok yönlü bir uygulama gelistirmek eski
yöntemlerle imkansız değildir ancak inanılmaz derecede zaman ve insan gücü
gerektirmektedir. Bu zorulukları asmak için gelisen teknolojiye ve isteklere paralel olarak
programlama dilleri de doğal gelisim içine girmistir. Bu yazıda son yıllarda is ve kisisel
uygulama gelistiricilerin adını sıkça duyduğu C# programlama dili ve diğer dillerle olan
iliskisi anlatılacaktır. C# programlama dilinin sunduğu imkanları anlatmaya baslamadan
önce programlama dillerinin tarihsel gelisimine göz atmak gerekir. Zira C# dili yıllardır
yoğun bir sekilde kullanılan C,C++ ve JAVA dillerinin temelleri üzerine kurulmustur. Sunu
da hemen belirtelim ki, son gelistirilen ilk gelistirilenden çoğu zaman daha iyi olacaktır.
Bu yüzden eski ile yeniyi karsılastırırken ticari amaçları bir kenara bırakıp objektif bir
gözle değerlendirmek gerekir.
C#’ı konusmadan önce C, C++ ve C# ile yakından iliskili olan JAVA’dan
bahsetmek gerekir.
C dili ve Yapısal Programlama
Düsündüklerimizi makinelere yaptırma isteğimizin bir sonucu olarak programlama
dilleri doğmustur. Makineleri anlamak insanoğlu için o kadar da kolay olmamıstır. Zira
makinelerin(bilgisayarların) anladığı dilden konusmak insanlar için gerçekten zor bir istir.
Gün geçtikçe makineleri anlamak ve onları programlamak için yeni arayıslar içine girildi.
Somutlastırılmıs makine komutları sayesinde bilgisayarları daha etkili bir sekilde
yönetmek mümkün hale gelmistir. Zaman ilerledikçe bilgisayarlar sadece belirli bilimsel
hesaplamaları yapmak için kullanılan araç olmaktan çıkıp insanların yasamlarında rutin
isleri yapabilecek araç haline geldi. Bilgisayarların insanların ihtiyaçlarına hızlı bir sekilde
cevap verebilmesi için onları hızlı bir sekilde programlamak gerekiyordu. Klasik
yöntemlerle(makine komutlarıyla) hızlı çözümler üretilemez hale gelince daha yüksek
seviyeli programlama dillerine ihtiyaç duyuldu. 1980’li yıllarda en çok kullanılan
programlama dili olan “C” bu anlamda atılmıs büyük bir adımdır. Yapısal programlama
modeli her ne kadar C dilinden önce de yapılıyor idiyse de asıl büyük gelismeler C dili ile
birlikte olmustur. C gibi makine diline göre yüksek seviyeli programlama dilleri ile büyük
projeler yapılabiliyordu. Artık uygulamalar sadece bilimsel çalısma aracı olmaktan çıkıp is
dünyasında kullanılabilen uygulamalar haline geldi. Bütün bu iyi gelismelerin yanında
zaman su gibi akıp gidiyordu, buna paralel olarak projeler büyüyor ve teknoloji artan
ivmeyle gelisiyordu. Yavas yavas anlasıldı ki C dili çok büyük projelerde yetersiz
kalıyordu. Yeni bir programlama modeline ihtiyaç duyuldu ve C++ dilinin temelleri atıldı.
C++ ve Nesne Yönelimli Programlama
Yapısal programlama modeliyle çok büyük projeleri kontrol altına almak neredeyse
imkansızdır. Bu sorunun üstesinden gelmek için yeni bir model gerekiyordu. Nihayet
Bjarne Stroustrup tarafından C dili baz alınarak yeni bir programlama dili gelistirildi. Bu
dilin adı : C++’tır. C++, C’nin üzerine insaa edildiği için ilk baslarda “C with
Classes”(Sınıflı C) olarak adlandırıldı. Peki bu dil C’den farklı olarak programcılara ne
sunuyordu? C++ dilinin sunduğu en büyük yenilik nesne yönelimli programlamayı
destekliyor olmasıdır. Nesne yönelimli programlama tekniği günümüzde de yaygın bir
sekilde kullanılan bir tekniktir. Bu teknik gerçek hayatı modellemede büyük bir basarı
sağlamaktadır. Söz gelimi bir projeyi parçalara ayrıp bu parçalar arasında programlama
yolu ile bağlantılar kurmak çok basit hale gelmistir. Nesne yönelimli programlama tekniği
proje gelistirme asamasında burada sayamayacağımız birçok kolaylık sağlamaktadır.
C++ dilinin diğer bir özelliğide C programcılarına hitap etmesiydi. C dilindeki temel
kurallar aynen C++ dilinde de mevcuttur. Bu yüzden C++ dilini ve nesne yönelimli
programlama tekniğine geçis yapmak için C dilini iyi bilmek gerekir. Daha doğrusu C++
dilini sadece nesne yönelimli programlamayı destekliyor seklinde düsünmemek gerekir.
Günümüzde birçok alt seviye islemlerde(haberlesme, isletim sistemi, aygıt sürücüleri)
C++ dilinin yoğun bir sekilde kullanılması bunun bir kanıtıdır.
Đnternetin Gelisimi ve JAVA Dili
Đnterneti’in gelisimi bilgisayar dünyasındaki en önemli ilerlemelerden biridir.
Programlama dünyasında JAVA dilinin ortaya çıkması en az internetin ilerlemesi kadar
önemlidir. Çünkü C ve C++ dilleri ile yalnızca belirli sistemlere yönelik uygulamalar
gelistirilebiliyordu. Oysa internet sayesinde birçok farklı sistem birbirine bağlanır hale
gelmistir. Artık sistemlerden bağımsız uygulama gelistirmek gerekiyordu. Daha doğrusu
interneti hedef alacak uygulama gelistirmek gerekiyordu. Programcılar gelisen internet
ortamına yabancı kalamazdı. Bu amaç doğrultusunda Sun Microsystems isimli firma
önceleri OAK olarak anılan JAVA isimli programlama dilini ortaya çıkardı. JAVA, dil olarak
C++ dilinin devamı gibi düsünülebilir. Ama amaç tamamen farklıdır. Zira Sun firması
ortaya JAVA dili ile birlikte yeni bir uygulama gelistirme modelide sunmaktaydı. Bu
programlama modelinde en büyük hedef sistemler arası tasınabilir kod yazmaktır. Yani
bir uygulamayı hem Microsoft platformunda hemde Unix ve Linux platformlarında
çalıstırabilmek hedeflenmistir. Böylece gelistirilen uygulamalar isletim sistemi ve
islemciden bağımsız hale gelecektir.
Peki sistemler arası bu yüksek tasınabilirlik nasıl olmaktadır? Cevabı basit : Ara
Dil. Evet, JAVA dilinde yazılmıs kodlar derlendiğinde kodlar makine komutların
çevrilmeden “ara kod” denilen “bytecode” a çevrilmektedir. Bytecode’a çevrilen program
çalıstırıldığında Java Sanal Makinesi devreye girer ve uygulamanın çalıstırıldığı sisteme
özgün makine kodunu üretir. Bu durumda Sun firmasının bir çok sistemde çalısabilecek
Java Sanal Makinesi üretmesi gerekiyordu. Nitekim zamanla günümüzde yaygın kullanılan
bütün sistemlerde sorunsuz çalısabilecek Java Sanal Makineleri gelistirildi. Hatta su an
için bazı cep telefonları ve çesitli sim kartlarında bile JAVA programlarını çalıstırabilecek
Java Sanal Makineleri mevcuttur.
JAVA ile C++ dili her ne kadar birbirine çok benzer olsada aynı kategoride değildir.
Elmayla armutu karıstırmamak gerekir. Eğer “JAVA mı C++ mı” diye bir soru sorulursa
cevap “her ikisi de” olacaktır. Çünkü ikisininde kullanım amacı farklıdır. Bir firma bir proje
için hiçbir zaman bu iki dilden birisini seçmek durumunda kalmayacaktır. JAVA ile aynı
kefeye koyabileceğimiz dil birazdan anlatacağım C# dilidir.
C# Dili ve .NET Platformu
JAVA’nın platform bağımsız kod üretmedeki basarısı su götürmez bir gerçektir. Bir
çok kurumsal dev projede JAVA dilinin ve J2EE platformunun olanaklarından
faydalanılması bunun en önemli göstergesidir. Günümüzde büyük projelerde birden fazla
programlama dili kullanılabilmektedir. Ancak JAVA’nın diller arası uyumlu çalısmaya
destek verememesi JAVA’nın bir eksikliği olarak görülmüstür. Diller arası uyumlu çalısma
alanında en büyük basarıyı Microsoft firması sağlamıstır. Son dönemlerde sıklıkla
kullanılan COM teknolojisi bu uyumluluğa bir örnektir. COM sayesinde farklı dillerde
yazılan yazılım parçacıkları diğer bir uygulamada kullanılabilmektedir.
JAVA’nın programlamadaki büyük bir bosluğu doldurması onun en büyük rakibi
olan Microsoft firmasının gözünden kaçmadı. En sonunda Microsoft’un bir ürünü olan
Visual Studio yazılım gelistirme aracına JAVA yı da ekleme kararı aldı. Visual J++ adı
altında Windows platformuna entegre edilen JAVA dili bu platformda pek basarılı olamadı.
Bu olusumun basarılı olmadığını gören Microsoft yeni arayıslar içine girdi. Microsoft
baskasının malını kendi ürününe entegre etmek yerine kendi ürününü gelistirmeye karar
verdi ve .NET yazılım gelistirme platformunu ortaya çıkardı. .NET temel felsefe olarak
J2EE platformuna benzemektedir ancak .NET’in derinliklerine daldıkça çok yeni
kavramlarla karsılasırız. Bu yeniliklerden en önemlisi “diller arası uyumluluk” tur. J2EE
platformunda sadece JAVA dili kullanılıyorken .NET platformunda birçok dil
kulanılabilmektedir. Bu dillerin sayısı oldukça fazladır. Üstelik Microsoft tarafından .NET
platformu için sıfırdan yeni bir dil tasarlanmıstır. Yapı olarak C++ ve JAVA dilllerine
benzerliği ile bilinen bu dil Anders Hejlsberg tarafından gelistirilen C# (C Sharp)’tan
baska bir sey değildir..
JAVA, C++ diline nasıl benziyorsa C# dilide C++ ve JAVA’ya benzemektedir.
Programlama modeli yine her üç ortamda da nesne yönelimlidir. Değisen sey bu modelin
uygulanıs seklidir. C++’ta kaynak kod derleyici tarafından makine koduna, JAVA’da
bytecode’a C#’ta ise IL(Intermediate Language-Ara Dil)’a çevrilmektedir. Burda
vurgulanması gereken en önemli nokta JAVA’da bytecode JAVA sanal makinesi tarafından
yorumlanarak çalıstırılırken, .NET’te IL kodları derlenerek çalıstırılmaktadır. Hemen sunu
da belirtelim ki, derleme islemi yorumlama isleminden performans açısından daha
öndedir.
C# dil olarak C++ ve JAVA’ya çok benzemektedir. Bu yüzden C# dilini konusurken
.NET platformunu göz önünde bulundurmalıyız. Dilleri sadece birer araç olarak
görmemizde fayda var. Đsterseniz lafı daha fazla uzatmadan JAVA/J2EE ve C#/.NET’i
karsılastırıp benzerliklerini ve farklılıklarını ortaya koyalım ardından C#’ı diğer .NET
dillerinden ayıran özellikleri inceleyip “neden C#” sorusuna cevap arayalım.
C# ile .NET mi JAVA ile J2EE mi?
Saf C# ve JAVA dilleri düsünüldüğünde birkaç nokta dısında bu iki dil birbirine
benzemektedir. Bu yüzden karsılastırma yaparken bu dillerin kullanıldıkları platformlarıda
göz önünde bulundurmak gerekir. Đsterseniz madde madde her bir özelliği iki platform
için değerlendirelim.
1-) Mimari : .NET ve J2EE çalısma biçimi olarak birbirine çok benzer. Her iki
platformda da uygulama kaynak kodu ara bir koda dönüstürülür. Aradaki en büyük fark
bu ara kodun isletilmesi sırasında görülür. .NET’te ara kod çalısma zamanında
derlendikten sonra çalıstırılırken JAVA’da yorumlanarak çalıstırılır.
2-) Çalısma Zamanı(Runtime) Mimarisi : J2EE platformundaki Java Sanal
Makinesi ile .NET platformundaki CLR(Common Language Runtime) birimi esdeğerdedir.
JVM, bytecode’un isletilmesinden sorumlu iken CLR, IL kodlarının isletilmesinden
sorumludur.
3-) Sistemler Arası Tasınabilirlik : Teorik olarak C# ve JAVA ile yazılmıs
uygulamalar sistemden bağımsızdırlar. Günümüzde C# ile .NET ortamında gelistirilen
uygulamaların bir çok mobil cihazda ve Windows sistemlerinde kullanıldığını düsünürsek
bu teorinin yavas yavas gerçeğe dönüstüğü görülebilir. Yakın bir gelecekte .NET
altyapısının Linux versiyonunun da çıkacağı bilinmektedir. JAVA ise bu konuda kendisini
çoktan kanıtlamıs durumdadır.
4-) Diller Arası Uyumluluk : J2EE platformunda sadece JAVA dili kullanılırken
.NET ortamında C#,C++,VB.NET ve hatta JAVA dili bile kullanılabilmektedir. Üstelik farklı
dillerde yazılmıs parçacıklar diğer bir dilde sorunsuzca kullanılabilmektedir. Bu sayede
bütün programcıların .NET platformunda rahat programlama yapabilmesi sağlanmıstır.
.NET uyumlu herhangi bir dilde gelistirilen bütün uygulamalar aynı ara koda
dönüstürüldüğü için .NET dilleri arasında büyük performans farklılıkları meydana gelmez.
5-) Web Servisi Kullanımı : Web Servisleri dağıtık yapıda gelistirilen
uygulamaların temel parçası olmustur. Özellikle iletisimin XML tabanlı olması web
servislerinin önemini göstermektedir. Her iki dil ile web servislerine erismek mümkün
olsada C# ile bir web servisini kullanmak oldukça kolaydır. C# ve .NET’in web servislerine
kolay erismesi bir avantaj olarak görülebilir.
6-) Bellek Yönetimi : C#’ta aynen JAVA’da olduğu gibi kullanılan nesneleri
toplama programcının görevi değildir. Kullanılmayan gereksiz nesneler gereksiz nesne
toplayıcısı tarafından zamanı geldiğinde bellekten silinirler. Buna rağmen C#
programcıları isterse belleği kendileri de yönetebilir. Yani C# dilinde bellek adreslerini
tutan göstericiler(pointer) hala kullanılabilmektedir. JAVA dilinde bu imkan yoktur. C#’ı
JAVA dan ayıran en büyük fark budur. Zira gösterici kullanımı sayesinde geriye dönük
uyumlulukta sağlanabilmektedir. Örneğin parametre olarak bir gösterici alan sistem
fonksiyonunu C#’ta kullanmak mümkündür.
7-) Veri Tipleri : C# dilinin temel felsefesi herseyin bir nesne olmasıdır. Temel
veri türleride dahil olmak üzere hersey birer nesne olarak tanımlanır. C# ve JAVA
sağladığı temel veri türleri bakımından birbirlerine çok yakındır.
8-) Tekrar Kullanılabilirlik : Nesne yönelimli programlama modelinin en önemli
özelliği gelistirilen sınıfların paketlenerek sonradan tekrar tekrar farklı uygulamalarda
kullanılabilmesidir. C#’ ta sınıflar isim alanları(namespace) içerisinde paketlenerek diğer
uygulamalar içinde kullanılabilir. Java’da ise sınıflar “package” dediğimiz bir kavramla
paketlenir. Sonuç olarak her iki dilde esit oranda bu özelliği desteklemektedir. Ancak
C#’ta sınıfların organizasyonu daha estetik bir sekilde düzenlenmektedir.
9-) Kontrol Mekanizmaları : Kodların içinde en çok görülen bloklar olan
for,while ve if gibi yapılar her iki dilde de vardır. C#’ta JAVA dilinde olmayan ayrıca
foreach döngüsü bulunmaktadır. foreach döngüsü ile koleksiyon tabanlı nesnelerin
elemanları arasında tek yönde rahatça dolasılabilmektedir.
10-) Türetme ve Çok Biçimlilik : Nesne yönelimli programlama modelinin C++
dilinden beri kullanılan mekanizmaları olan türetme ve çok biçimlilik her iki dilde de
mevcuttur. C++’tan farklı olarak C# ve Java’da sadece tekli türetme mevcuttur.
11-) Đstisnai Durumları Yönetme : Uygulamların en büyük düsmanı olan
istisnai durumların(exceptions) her iki dilde de ele alınıs biçimi hemen hemen aynıdır.
12-) Sınıf Kütüphanesi : Veritabanı ve dosya islemleri gibi burada
sayamayacağımız bir çok temel isi yapan sınıflar .NET ve J2EE platformunda mevcuttur.
Gerek bu sınıfların organizasyonu gerekse de sınıfların kullanılıs biçimi bakımından .NET
platformunun daha avantajlı olduğunu söyleyebiliriz.
Bütün bu maddeler bir bütün olarak ele alındığında C#’ın JAVA’dan bir kademe
önde olduğu görülmektedir. Bu durum elbette proje yöneticilerinin seçimlerini
etkilemektedir. Microsoft faktörünüde göz önünde bulundurursak C# ve .NET’in gelecekte
çok is yapacağını söylemek için müneccim olmaya gerek yok. Bu arada JAVA’nın halen
yaygın bir sekilde kullanıldığını da gözardı etmemeliyiz. Bu durum C# ve JAVA’nın
seçiminde sadece teknik özelliklerin değil aynı zamanda Windows ve Linux’te olduğu gibi
sosyal etkenlerinde rolü bulunduğunu gösteriyor.
Buraya kadar söylediklerimden belki söyle bir soru isareti doğmus olabilir : “C# mı
JAVA mı” sorusunu “C# mı C++ mı” seklinde sorsak neler değisir? Cevap : Çok sey
değisir. Evet C#’ın JAVA ile olan iliskisi C++ ile olan iliskisinden tamamen farklıdır. C# ile
JAVA’yı ancak saf dil olarak karsılastırabiliriz. Yani dilin sentaksından bahsediyorum. Bu
iki dilin kullanıldığı ortam farklıdır. Birinde bir sisteme özgün makine kodu üretilirken
diğerinde sistemden bağımsız ara bir kod olusturulmaktadır. Bu durumda C++ ve C#’ı bir
bütün olarak karsılastırmayı kisisel olarak doğru bulmuyorum. Çünkü ikisi farklı
kategorilerde yarısıyor. Eğer bir gün .NET’in ürettiği ara koddaki komutlar ile çalısan
mikroislemci gelistirilirse o zaman belki C# ile C++’ı karsılastırabiliriz. Peki C# mı C++?
Cevap : Her ikiside. Eğer sirketiniz Intel islemciler için bir isletim sistemi gelistiriyorsa
elbette C++ ve C dilleri seçilmelidir. Sirketiniz dağıtık yapıda çok genis bir çalısma ağı
olan bir uygulama gelistiriyorsa o zaman C# ve .NET’i seçmeniz daha doğru olacaktır. Bu
seçim bir projede hangi dilin kullanılacağını değerlendirmek içindi. Đse bir de programcılar
açısından bakalım. Bir programcının hem C++ hem C# hemde JAVA bilmesine gerek var
mı? Bence gerek var yada yok. Kesin bir cevabı verilemez bu sorunun. Daha doğrusu bir
programcı ihtiyaç dahilinde herhangi bir programlama dilini kullanabilmelidir. Ancak sunu
da unutmayalım ki iyi bir programcı çok sayıda programlama dili bilen demek değildir. Đyi
bir programcı .NET platformunda olduğu gibi programlama dilinden bağımsız kod
üretebilmelidir.
Diğer .NET Dilleri ve C#
Daha öncede dediğim gibi .NET paltformunda bir çok programlama dilini
kullanabiliriz. Bu dillerin en önemlileri C#, VB.NET, C++.NET ve J# dilleridir. Bu dillerden
bir tanesinin özel bir konumu vardır. Tahmin edeceğiniz gibi bu dil C#’tır. C# .NET
platformu için sıfırdan gelistirilmis yeni bir dildir. Diğer diller ise eski versiyonları
değistirilerek .NET’e uyumlu hale getirilmistir. Özellikle Visual Basic dilinin devamı gibi
görünen VB.NET dilinde bir çok radikal değisiklik yapılmıstır. Örneğin VB dili nesne
yönelimli programlama tekniğini destekler hale getirilmistir. Bu eklentilerin çok basarılı
oloduğu söylenemez. Çünkü bu sekildeki zoraki eklentiler dilin en basta tasarlanma
amacına uygunluğunu ortadan kalkmaktadır. Bu amaçla Microsoft, hem nesne yönelimli
programlama tekniğine tam destek veren, C++ dilinin güçlü özelliklerinden yoksun
olmayan ve aynı sekilde Visual Basic dilinin kolaylığından esinlenerek C# dilini çıkardı.
Peki .NET dilleri arasında C#’ı tercih etmemize neden olacak baska neler var? Her
seyden önce C# öğrenilmesi kolay bir dildir. Az sayıda anahtar sözcük içermesine rağmen
bir çok olanağı programcının hizmetine sunmustur. C# nesne yönelimli programlama
diline tam destek verdiği içinde seçilebilir. C#’ta değisken kavramı neredeyse kalkmıstır.
Bunda bütün temel veri türleri de dahil olmak üzere bütün sınıfların Object diye
adlandırılan bir sınıftan türetilmesinin etkisi vardır. C# dili güç ve hızlılık arasındaki
dengeye estetik bir sekilde korumaktadır. Temsilci ve olaylarla VB’deki olay mantığına
benzer bir model sunarken aynı zamanda göstericileri kullanmaya imkan vererek C++
dilinin güçlü özelliklerinden yoksun bırakmamıstır. .NET sınıf kütüphanesinin büyük bir
kısmı C# ile gelistirilmistir. Yani bu kütüphaneyi en etkin biçimde C# ile kullanabiliriz.
Dahası C# dili .NET’in çalısma mimarisi de gözönünde bulundurularak sıfırdan tasarlandığı
için .NET’in bütün olanaklarından en etkin biçimde C# ile faydalanabiliriz.
C# için söylenebilecek son söz : C#, modern programlama tekniklerine tam
destek veren, internet çağının gerektirdiği tüm yazılım bilesenlerini gelistirmeye izin
veren, hızlı ve etkin bir sekilde kodlama yapılabilen, C++ ve JAVA’nın güzel yönlerini alıp
geriye dönük uyumluluğu JAVA’da olduğu gibi gözardı etmeyen bir programlama dilidir.
Sonuç
Đnternet’in ve haberlesme teknolojisinin çok ileri bir seviyede olduğu bir dönemde
internet üzerinde kullanılabilecek yazılım bilesenlerini programlamak son derece önem
kazanmıstır. Her ne kadar C# ve JAVA öncesi dillerle hersey yapılabiliyor olsada
projelerin boyutlarının büyümesi bu dillerin artık yetersiz olduğunun bir göstergesidir.
Özellikle yeni nesil is uygulamalarında C# ve JAVA, C++’tan bir adım önde görünüyor.
Tabi bu durum C++ dilinin kötü olduğunu göstermez. Nitekim C# ve JAVA dillerinin her
ikiside C++ dilini örnek almıstır. Değisen tek sey günün ihtiyaçlarıdır. Aynı zamanda C#
dili JAVA, C++.NET, VB.NET ve J# gibi diller önünde de bir adım önde görünüyor.
MD5 ile Veri Sifreleme
Bu makalemizde herhangi bir string ifadenin nasıl MD5 ile sifreleneceğini öğreneceğiz. Bu
sırada web.config, Panel Nesnesi, Stored Procedure gibi konulara da değineceğiz.
Asağıda verdiğim örnek, çoğu zaman kullandığımız Kayıt Formu ile Login Formundan
olusuyor. Kayıt olurken, email adresi ve parola bilgileri soruluyor. Bunun sonrasında
parola bilgisi MD5 algoritması ile sifrelenip veritabanına veriler yazılıyor.
Login Formumuzda ise, aynı veriler istenerek, yine parolamız MD5 algoritması ile
veritabanına gönderiliyor. Yani SQL'deki "Select" cümlesi aracılığı ile kontrolümüzü
yapıyoruz.
Örneğimize geçmeden önce örneğimiz içerisinde kullandığımız Panel nesnemizin bazı
özelliklerini inceleyelim.
Height = Panelimizin yüksekliği (pixel cinsinden)
Width = Panelimizin genisliği (pixel cinsinden)
BackColor = Panelimizin arkafon rengi
BackImageUrl = Panelimizin arkasında resim göstermek istiyorsak
BorderColor = Panelimizin sınır çizgisinin rengi
BorderWidth = Panelimizin sınır çizgisinin genisliği (pixel cinsinden)
Font = Panelimizin içerisinde gösterilecek metinlerin Font adı
Visible = Panelimizin görüntülenme ayarı (true/false değerleri alır)
Simdi de veritabanına bağlanmak amaçlı kullandığımız bağlantı satırımızı nasıl
kullandığımıza bakalım.
Klasik ASP içerisinde veritabanına bağlanmak istediğimizde bunu çoğu zaman asp
dosyamızın içerisine yazıyorduk. Veya baska bir sayfaya yazıp, onu kullanacağımız
sayfaya dahil ediyorduk. Hatırlarsanız bu yönteme "Include File" yöntemi deniyordu. Bu
durum güvenlik açısından birçok açık ortaya çıkartmak ile beraber, yetersiz de kalıyordu.
.Net'te ise bu sıkıntılar atlatıldı. Simdi projemiz ile ilgili birçok veriyi saklayabileceğimiz,
güvenli bir dosyaya kavustuk. Đste bu dosyanın adı web.config
Web.config dosyasının ayrıntılarını burada isleyemeyeceğim. Sadece veritabanı bağlantı
satırımızı nasıl web.config sayfamıza yazmamız gerektiğini ve aspx dosyamızdan nasıl
çağırıldığını göstereceğim.
Örneğin web.config dosyamızın içeriği:
<configuration>
<appSettings>
<add key="strConn"
value="server=localhost;uid=dtuser;pwd=dtpass;database=dotnet" />
</appSettings>
</configuration>
Gelelim aspx dosyamızdan nasıl çağırabileceğimize:
string dbConnStr = ConfigurationSettings.AppSettings["strConn"];
Sanırım artık konumuza dönebiliriz. Đlgili tüm açıklamaları kod satırları arasında
anlatmağa çalıstım. Ayrıca CodeBehind yönetimi kullanarak kodladım. Bu yöntemden de
kısaca bahsetmek gerekirse, CodeBehind yöntemi ile kodumuz ile görselliğimizi tamamen
ayırıyoruz. Böylelikle tasarım değisikliği gibi durumlarda hiçbir sıkıntı çekmiyoruz. Örneği
incelediğinizde durumu da farkedeceksiniz.
Fakat öncelikle, veritabanamızın yapısını, stored procedure ve web.config dosyamızın ilgili
kodlarını verelim.
Tablomuz:
registerUser
user_id int (IDENTITY)
user_email varchar 255
user_password binary 16
Stored Procedure:
sp_ins_regUser
CREATE PROCEDURE sp_ins_regUser
@user_email varchar(255),
@user_password binary(16)
AS
INSERT INTO
registerUser
(user_email, user_password)
VALUES
(@user_email, @user_password)
GO
sp_sel_loginCheck
CREATE PROCEDURE sp_sel_loginCheck
@uemail varchar(255),
@upwd binary(16)
AS
SELECT
user_id
FROM
registerUser
WHERE
user_email = @uemail AND
user_password = @upwd
GO
Web.config
<configuration>
<appSettings>
<add key="strConn"
value="server=localhost;uid=dtuser;pwd=dtpass;database=dotnet" />
</appSettings>
</configuration>
Đlk önce görsel arayüzümün bulunduğu, kisinin kayıt olduğu sayfa olan:
register.aspx
<%@ Page Language="c#" Inherits="registerForm.regForm" Src="register.aspx.cs"%>
<html>
<body>
<asp:Panel id="register" runat="server">
<form id="regForm" method="post" runat="server">
<table cellspacing="0" cellpadding="3" border="0">
<tr>
<td colspan="2">Kayıt Formu</td>
</tr>
<tr>
<td><b>E-posta Adresiniz</b></td>
<td><asp:TextBox id="emailAdr" runat="server"
MaxLength="255"></asp:TextBox></td>
</tr>
<tr>
<td><b>Parolanız</b></td>
<td><asp:TextBox runat="server" MaxLength="10" id="parola"
TextMode="Password"></asp:TextBox></td>
</tr>
<tr>
<td align="right" colspan="2"><asp:Button id="btnOk"
OnClick="doRegister" runat="server" text="Formu Gönder"/></td>
</tr>
</table>
</form>
</asp:Panel>
<asp:Panel id="registerStatus" runat="server">
<asp:Label id="lblInfo" runat="server"></asp:Label>
</asp:Panel>
</body>
</html>
Bu sayfanın kodlarını isleyen:
register.aspx.cs
using System;
using System.IO;
using System.Web.UI; //web textbox larına ulasabilmemiz için gereken class
using System.Security.Cryptography; //md5 için gerekli class
using System.Text; //UTF fonksiyonu için gerekli class
using System.Data; //veritabanı islemleri için gerekli class
using System.Data.SqlClient; //veritabanı islemleri için gerekli class
using System.Configuration; //web.config dosyamızdan veri okuyabilmek amaçlı class
namespace registerForm
{
public class regForm : System.Web.UI.Page
{
//kodlamada kullanacağımız nesnelerimizi tanımlıyoruz.
protected System.Web.UI.WebControls.TextBox emailAdr;
protected System.Web.UI.WebControls.TextBox parola;
protected System.Web.UI.WebControls.Panel register;
protected System.Web.UI.WebControls.Panel registerStatus;
protected System.Web.UI.WebControls.Label lblInfo;
public void Page_Load(Object Src, EventArgs E)
{
//sayfa yüklendiğinde panellerimizin görüntülenme ayarlarını
yapıyoruz.
//form ekranı ilk olarak görüntülenecek.
register.Visible = true;
registerStatus.Visible = false;
lblInfo.Text = "";
}
//Formu Gönder butonuna tıklandığında çalısan fonksiyonumuz
protected void doRegister(object sender, System.EventArgs e)
{
//girilen verileri alıyoruz.
string txtEmailAdr = emailAdr.Text;
string txtParola = parola.Text;
//islem sonucu göstermek amaçlı ikinci panelimizi görünür
kılıyoruz.
register.Visible = false;
registerStatus.Visible = true;
try
{
//parolanız sifrelenmesi için fonksiyona gönderiyoruz.
//sifrelenmis verimiz byte haline geleceği için
değiskenimizi
//byte olarak tanımlıyoruz.
byte[] encyrptedPassword =
md5Password(txtParola);
//veritabanına Email adresini ve sifrelenmis Parolayı
kayıt ediyoruz.
SqlConnection conn = new
SqlConnection(ConfigurationSettings.AppSettings["strConn"]);
conn.Open();
SqlCommand sc = new SqlCommand ();
sc.Connection = conn;
sc.CommandType =
CommandType.StoredProcedure;
sc.CommandText = "sp_ins_regUser";
sc.Parameters.Add("@user_email",
SqlDbType.VarChar, 255, "user_email");
sc.Parameters["@user_email"].Value = txtEmailAdr;
sc.Parameters.Add("@user_password",
SqlDbType.Binary, 16, "user_password");
sc.Parameters["@user_password"].Value =
encyrptedPassword;
sc.ExecuteNonQuery();
conn.Close();
//try-catch bloğuna soktuğumuz islemimizde bir
sorun çıkmadı ise
//ziyaretçimizi bilgilendiriyoruz.
lblInfo.Text = "Kayıt isleminiz basarı ile
gerçeklestirilmistir";
}
catch
{
//veritabanında bir hata olustuysa ziyaretçimizi
bilgilendiriyoruz.
lblInfo.Text = "Kayıt isleminiz sırasında bir hata
olustu. Lütfen tekrar deneyiniz.";
}
}
byte[] md5Password(string pass)
{
//md5 sifrelenmesi için verimizin byte haline gelmesi gerekli.
//veri 8-bit seklinde dönüstürülmesi için ilk önce UTF
fonksiyonuna gönderiliyor.
UTF8Encoding encoder = new UTF8Encoding();
//md5 sifrelemesi için nesnemizi olusturuyoruz.
MD5 md5 = new MD5CryptoServiceProvider();
//verimizi md5 ile çalıstırıyoruz.
//dikkat ederseniz UTF fonksiyonundan dönen değeri GetBytes
ile alabildik.
byte[] donenDeger =
md5.ComputeHash(encoder.GetBytes(pass));
//sifrelenmis değerimizi geri gönderiyoruz.
return donenDeger;
}
}
}
Kullanıcı adı, parola verilerinin girildiği görsel sayfa olan:
login.aspx
<%@ Page Language="c#" Inherits="loginForm.Login" Src="login.aspx.cs"%>
<html>
<body>
<asp:Panel id="loginStatus" runat="server">
<asp:Label id="lblInfo" runat="server"></asp:Label>
</asp:Panel>
<asp:Panel id="pnlLogin" runat="server">
<form id="loginForm" method="post" runat="server">
<table cellspacing="0" cellpadding="3" border="0">
<tr>
<td colspan="2">Siteye Giris</td>
</tr>
<tr>
<td><b>E-posta Adresiniz</b></td>
<td><asp:TextBox id="emailAdr" runat="server"
MaxLength="255"></asp:TextBox></td>
</tr>
<tr>
<td><b>Parolanız</b></td>
<td><asp:TextBox id="parola" runat="server"
MaxLength="10" TextMode="Password"></asp:TextBox></td>
</tr>
<tr>
<td align="right" colspan="2"><asp:Button id="btnLogin"
OnClick="doLogin" runat="server" text="Formu Gönder"/></td>
</tr>
</table>
</form>
</asp:Panel>
</body>
</html>
login.aspx dosyamızı isleyen sayfamız:
login.aspx.cs
using System;
using System.IO;
using System.Web.UI; //web textbox larına ulasabilmemiz için gereken class
using System.Security.Cryptography; //md5 için gerekli class
using System.Text; //UTF fonksiyonu için gerekli class
using System.Data; //veritabanı islemleri için gerekli class
using System.Data.SqlClient; //veritabanı islemleri için gerekli class
using System.Configuration; //web.config dosyamızdan veri okuyabilmek amaçlı class
namespace loginForm
{
public class Login : System.Web.UI.Page
{
//kodlamada kullanacağımız nesnelerimizi tanımlıyoruz.
protected System.Web.UI.WebControls.TextBox emailAdr;
protected System.Web.UI.WebControls.TextBox parola;
protected System.Web.UI.WebControls.Panel pnlLogin;
protected System.Web.UI.WebControls.Panel loginStatus;
protected System.Web.UI.WebControls.Label lblInfo;
public void Page_Load(object sender, System.EventArgs e)
{
//sayfa yüklendiğinde panellerimizin görüntülenme ayarlarını
yapıyoruz.
//form ekranı ilk olarak görüntülenecek.
pnlLogin.Visible = true;
loginStatus.Visible = false;
lblInfo.Text = "";
}
//Formu Gönder butonuna tıklandığında çalısan fonksiyonumuz
protected void doLogin(object sender, System.EventArgs e)
{
//girilen verileri alıyoruz.
string uid = emailAdr.Text;
string pwd = parola.Text;
//islem sonucu göstermek amaçlı ikinci panelimizi görünür
kılıyoruz.
pnlLogin.Visible = false;
loginStatus.Visible = true;
//parolanız sifrelenmesi için fonksiyona gönderiyoruz.
//sifrelenmis verimiz byte haline geleceği için değiskenimizi
//byte olarak tanımlıyoruz.
byte[] encyrptedPwd = md5Password(pwd);
//veritabanına ilgili verileri göndererek kontrolümüzü yaparız.
SqlConnection conn = new
SqlConnection(ConfigurationSettings.AppSettings["strConn"]);
conn.Open();
SqlCommand sc = new SqlCommand ();
sc.Connection = conn;
sc.CommandType = CommandType.StoredProcedure;
sc.CommandText = "sp_sel_loginCheck";
sc.Parameters.Add("@uemail", SqlDbType.VarChar, 255,
"uemail");
sc.Parameters["@uemail"].Value = uid;
sc.Parameters.Add("@upwd", SqlDbType.Binary, 16, "upwd");
sc.Parameters["@upwd"].Value = encyrptedPwd;
try
{
//Elimizdeki verilerle çalıstırdığımız SP'mizden geri bir
kolon, bir satır döndüğü
//için SqlCommand nesnesinin ExecuteScalar()
metodunu kullanıyoruz.
//user_id suanda bizim isimize yaramıyor, fakat nasıl
çekildiğini göstermek amacı ile
//bu satırı da kodumuza ekledim.
string user_id = sc.ExecuteScalar().ToString();
//Email ve parola doğru ise bilgilendiriyoruz.
lblInfo.Text = "Hosgeldiniz ";
}
catch
{
//Email ve parola yanlıssa tekrar girmesini istiyoruz.
lblInfo.Text = "Yanlıs E-posta Adresi/Parola. Lütfen
bilgilerinizi kontrol edip tekrar deneyiniz.";
}
conn.Close();
}
byte[] md5Password(string pass)
{
//md5 sifrelenmesi için verimizin byte haline gelmesi gerekli.
//veri 8-bit seklinde dönüstürülmesi için ilk önce UTF
fonksiyonuna gönderiliyor.
UTF8Encoding encoder = new UTF8Encoding();
//md5 sifrelemesi için nesnemizi olusturuyoruz.
MD5 md5 = new MD5CryptoServiceProvider();
//verimizi md5 ile çalıstırıyoruz.
//dikkat ederseniz UTF fonksiyonundan dönen değeri GetBytes
ile alabildik.
byte[] donenDeger =
md5.ComputeHash(encoder.GetBytes(pass));
//sifrelenmis değerimizi geri gönderiyoruz.
return donenDeger;
}
}
}
C# ve GDI+ Kullanılarak Yapılan DXBALL Oyunu
Bu yazida anlatacagim oyun, bazilarinin DXBall, bazilarinin Alleyway… diye bildigi bir çok
ismi olan bir oyun.
Oyunun tüm dosyalari burada verilmeyecktir ancak .NET ile yeni bir proje açilip .cs
dosyalari kopyalanirsa, oyun çalisacaktir. Ayrica oyunda kullanilan top,blok sekilleri için
gerekli olan resimleri de istediginiz gibi seçebilirsiniz. Top resminin dosya adi tp.bmp,
bloklarin dosya adi brick.bmp, alttaki cubuk resminin dosya adi ise blk.bmp olmak
zorundadir.'dir. Oyunu normal haliyle çalistirmak istiyorsaniz download edebilirsiniz.
(Bilgisayarda .NET ve Framework 1.1 kurulu olmak zorunda! )
Programi derleyip çalistirdiginizda oyunun ekran görüntüsünün asagidaki gibi oldugunu
göreceksiniz.
Oyunun kaynak kodunu indirmek için tiklayin.
Önemli: Siniflarin neler oldugunu uzun uzun paragraflar halinde anlatmaktansa kodun
yaninda komut satirlariyla anlatilacaktir.
Simdi adim adim oyunu açiklayalim;
o Ilk olarak Top.cs dosyamiz var. top sinifimizda, en önemli metot olan move
metodunda topun koordinatlarina bakilir ve koordinatlara göre ne tarafa
hareket ettirilecegi belirlenir.
Top.cs:
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace alleyway
{
public class Top
{
public Point pos; //topun koordinatlarini tutmaya yarayacak.
private Bitmap tp=null; //topun resmi için gerekli.
public int Xhareket=4; //topun x'de hareket hizini burada belirleyecegiz.
public int Yhareket=4; //topun y'de hareket hizini burada belirleyecegiz.
public Top(int x,int y)
{
pos.X = x; //ana formdan gelen topun nerede baslatilacagi bilgisi
pos.Y = y; // burada x ve y koordinatlari olarak girilir.
if (tp==null) //eger resim yüklenmediyse yükleme islemi yapilacak.
{
tp = new Bitmap("tp.bmp");
}
}
public Rectangle GetFrame()
{
Rectangle myRect = new Rectangle(pos.X, pos.Y, tp.Width, tp.Height);
return myRect; //top nesnemizin konumunu dikdörtgen olarak belirlememize yarar.
} // bu sayede top ile bloklarin çarpisip çarpismadigini daha kolay
//anlayacagiz.
public void Draw(Graphics g)
{
g.DrawImage (tp,pos.X,pos.Y,tp.Width ,tp.Height ); //top nesnesi ekrana
//çizdirilir. X,y koordinatlarindan sonra ebatlari parametre olarak girilir.
}
public void Remove(Top t)
{
Yhareket=-Yhareket;
pos.X += Xhareket;
pos.Y += Yhareket;
}
public void Move(int hak,Grup grp,Top t,Rectangle r, Rectangle rblok)
{
//topun pencere icinde duvarlara ve cubuga carptikça simetrik olarak sekmesini
//burada saglayacagiz.
if(t.GetFrame().IntersectsWith(rblok)) //eger top bloka çarptiysa,
{
Yhareket=-Yhareket; // topun y yönünü ters cevir
} // böylelikle sekmeyi sagla.
//top ekrandaysa,
if(pos.X > 539 || pos.X < 0)
{
//topun x hareketini ters cevir
Xhareket = -Xhareket;
}
// top yukari carparsa
if(pos.Y < 0)
{
//y hareketini degistir
Yhareket = -Yhareket;
}// yanarsa,
else if(pos.Y > 500)
{
//top durur
Xhareket = 0;
Yhareket = 0;
//hakki 1 azalt
hak -= 1;
}
// top koordinatlarini degistir
pos.X += Xhareket;
pos.Y += Yhareket;
}
}
}
o Blok.cs sinifi ise alt taraftaki yönlendirecegimiz cubuktur.
Blok.cs:
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace alleyway
{
public class Blok
{
public Point pos; //x ve y koordinatlarini belirlemek için kullanilacak.
static Bitmap Cubuk = null; //Çubuk resmi için kullanilacak.
int inc = 6; //çubugun sürükleme hizini belirleyecek
int LastposX = 0; //su anda son pozisyon olmadigindan ikisi de sifirlanir.
int LastposY = 0;
public Blok(int x, int y)
{
pos.X = x; //x konumu verilir.
pos.Y = y; /y konumu verilir.
if (Cubuk == null) //çubuk ekranda yoksa,
{
Cubuk = new Bitmap("blk.bmp"); //çubuk ekrana çizdirilir.
}
}
public Rectangle GetFrame()
{
Rectangle myRect = new Rectangle(pos.X, pos.Y, Cubuk.Width, Cubuk.Height); //top
sinifindaki islevi görür.
return myRect;
}
public void Draw(Graphics g)
{
Rectangle destR = new Rectangle(pos.X, pos.Y, Cubuk.Width, Cubuk.Height); //çubuk
hareketlerinde kullanilacak.
Rectangle srcR = new Rectangle(0,0, Cubuk.Width, Cubuk.Height);
g.DrawImage(Cubuk, destR, srcR, GraphicsUnit.Pixel);
LastposX = pos.X;
LastposY = pos.Y;
}
public void MoveLeft(Rectangle r)
{
if (pos.X <= 0) //eger pencerenin en solundaysa hareket etmeyecek, degilse sola dogru
hareketine devam edecektir.
return;
pos.X -= inc;
}
public void MoveRight(Rectangle r)
{ //eger pencerenin en sagindaysa hareket etmeyecek, degilse saga dogru hareketine
devam edecektir.
if (pos.X >= r.Width - Cubuk.Width)
return;
pos.X += inc;
}
}
}
o Brick.cs sinifi ise patlatilacak bloklari temsil eder. Burada sadece basit
özellikleri tutlur. Bloklari kontrol eden Grup sinifinda tüm bloklari ayni anda
kontrol edildigini göreceksiniz. Grup sinifinda tüm bloklari tutan bir matris
bulunmaktadir.
Brick.cs:
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace alleyway
{
public class Brick
{
public int yukseklik,genislik,basx,basy; //bloklarin özellikleri burada tutulur.
public bool vuruldu=false; //top tarafindan vurulup vurulmadigi buradan anlasilacaktir.
public Brick(int yuk,int gen,int startx,int starty)
{
this.yukseklik=yuk;
this.genislik=gen;
this.basx=startx;
this.basy=starty;
this.vuruldu=false; //yeni yaratildigi için vurulmadi oalarak isaretlenir.
}
}
}
o Grup.cs sinifinda ise blok kontrolü yapilir.
Grup.cs:
using System;
using System.Drawing;
using System.Drawing.Imaging;
namespace alleyway
{
public class Grup
{
Bitmap br=null;
public Brick[,] yapi; //brick sinifinda nesneleri tutan matrisimiz burada yaratiliyor.
int sut=5; //bloklarin sütun ve satir sayilari bu kisimda girilir.
int sat=8;
public Grup()
{
reset(); //vurulmus duurmda olan bloklar varsa bunlari da vurulmadi olarak isaretleyen
ve ilk haline döndüren metod çagrilir.
}
public void Draw(Graphics g)
{
for (int i=0;i<SAT;I++)< font>
{ // bu iç içe for döngüleri bölümünde bloklar tek tek dolasilarak
for (int j=0;j< çizdirilmez) yapilir(ekrana görünmez varsa olan vurulmus>
{ // vurulmayanlar ise ayni sekilde ekrana çizdirilir.
if (yapi[i,j].vuruldu==false)
{
Brick temp= (Brick) yapi[i,j];
g.DrawImage(br,temp.basx,temp.basy,br.Width,br.Height);
}
}
}
}
public void reset(){ //tüm bloklar tekrar olusturulur.
yapi = new Brick[sat,sut];
if (br==null)
{
br = new Bitmap("Brick.bmp");
}
for (int i=0;i< olarak vurulmadi bloklar dolasilarak matris>
{
for (int j=0;j<SUT;J++)< font>
{
yapi[i,j]=new Brick(br.Height,br.Width,(i) * 65 +20, (j * 35) + 50);
yapi[i,j].vuruldu=false;
}
}
}
}
}
o Form1.cs,oyunun ana formudur yani ekranimizdir. Bu formda esas önemli
olan yerler açiklanacaktir.
Form1.cs:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Threading;
using System.Runtime.InteropServices;
namespace alleyway
{
public class Form1 : System.Windows.Forms.Form
{
public int Hak=3; //kaç hakkimiz oldugunu belirleriz.
int puan=0; //puan baslangiçta sifirdir.
private bool flag = true;
private Blok blok = new Blok(275, 490); //alttaki çubugun yeri belirlenir.
private Top top = new Top(250, 300); // topun konumu belirlenir.
private Grup grup=new Grup();
private System.Windows.Forms.Timer timer1;
private System.ComponentModel.IContainer components;
public Form1()
{
InitializeComponent();
timer1.Start(); //top hareketleri ve ekran degisimleri bu timer sayesinde
belirlenir. Her 50 salisede ekran tekrar
//çizdirilir.
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
}
protected override void Dispose( bool disposing ) //bu kodlari .net kendisi yaratir.
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
this.timer1 = new System.Windows.Forms.Timer(this.components);
// timer1 öezlliklerini burada tanimlariz.
this.timer1.Enabled = true;
this.timer1.Interval = 50;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
// Form1 için yapilan degisiklier buradadir.
this.AutoScaleBaseSize = new System.Drawing.Size(6, 16);
this.ClientSize = new System.Drawing.Size(550, 500);
this.Font = new System.Drawing.Font("Comic Sans MS", 8.25F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.KeyPreview = true;
this.Location = new System.Drawing.Point(150, 200);
this.MaximizeBox = false;
this.Name = "Form1";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "PatLat";
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
this.Load += new System.EventHandler(this.Form1_Load);
this.Paint += new System.Windows.Forms.PaintEventHandler(this.Form1_Paint);
}
#endregion
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
g.FillRectangle(Brushes.Black, 0, 0, this.ClientRectangle.Width, ClientRectangle.Height);
blok.Draw(g); //oyunumuzdaki tüm nesneler ekrana çizdirilir.
top.Draw (g);
grup.Draw(g);
}
private void Form1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{ //bu kisimda tusa basilinca çubugun hareket ettirilmesi gerçeklestirilmistir.
Case yapisi içinde sag saol hareketleri
//yapilir.
string result = e.KeyData.ToString();
Invalidate(blok.GetFrame());
switch (result)
{
case "Left": //sol tusuna basildiysa sola git
blok.MoveLeft(ClientRectangle);
Invalidate(blok.GetFrame()); //tekrar blogu çizdir.
break;
case "Right": //sag tusuna basildiysa saga git
blok.MoveRight(ClientRectangle);
Invalidate(blok.GetFrame());//tekrar blogu çizdir.
break;
default:
break;
}
}
private void timer1_Tick(object sender, System.EventArgs e)
{ //timer 'in her tikinde top ve bloklar kesisti mi? kontrolü yapilir ve kesisme
varsa o blok vuruldugu için ekrandan silinir.
flag=true;
for (int i=0;i<8;i++)
{
for (int j=0;j<5 ;j++)
{
Rectangle r_brick = new
Rectangle(grup.yapi[i,j].basx,grup.yapi[i,j].basy,grup.yapi[i,j].genislik,grup.yapi[i,j].yuks
eklik);
if(top.GetFrame().IntersectsWith(r_brick))
{
if (grup.yapi[i,j].vuruldu==false)
{
grup.yapi[i,j].vuruldu=true;
top.Remove(top); //ekrandan topun silinmesi.
puan=puan+10; //blok vuruldu, puani arttir.
flag=false;//yanmadigimizi belirtmek için flag false yapilir
}
}
}
}
if (flag) //flag true ise yandi demektir.
{
top.Move (Hak,this.grup,this.top,ClientRectangle, blok.GetFrame ());
if (top.pos.Y>500)
{
timer1.Stop(); //hareketi durdur.
Hak--; //hakki 1 azalt
if (Hak==0) //eger hak sifir ise,
{
Form3 form3=new Form3(puan); //oyun bitti ekranini göster.
form3.ShowDialog(this);
if (form3.DialogResult==DialogResult.OK) // O ekranda tekrar oynaya basilirsa,
{
Hak=3; //hakki 3'e çikar,
puan=0; //puani sifirla ve tekrar baslat.
top.pos.X=265;
top.pos.Y=300;
blok.pos.X=275;
blok.pos.Y=490;
grup.reset();
top.Xhareket=-top.Xhareket;
Invalidate();
timer1.Start();
}
}
else //hak sifir olmadiysa,
{
Form2 form2=new Form2(Hak); //kaç hakki oldugunu, puanini göster,oyuna devam
et
form2.ShowDialog(this);
top.pos.X=250;
top.pos.Y=250;
blok.pos.X=275;
blok.pos.Y=490;
top.Xhareket=-top.Xhareket;
Invalidate();
timer1.Start();
}
}
}
flag=true;
Invalidate();
}
}
}
o Simdi kodunu göreceginiz iki form ise oyun bitince ve yaninca gösterilen
formlar oldugundan açiklanacak bir bölüm içermemektedirler.
Oyun Bitince gösterilen form:
Form3.cs:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace alleyway
{
///
/// Summary description for Form3.
///
public class Form3 : System.Windows.Forms.Form
{
int ppuan;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Button OK;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
public Form3(int puan)
{
//
// Required for Windows Form Designer support
//
ppuan=puan;
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.OK = new System.Windows.Forms.Button();
this.button2 = new System.Windows.Forms.Button();
this.label3 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.Font = new System.Drawing.Font("Bookman Old Style", 15.75F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.label1.Location = new System.Drawing.Point(48, 8);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(144, 32);
this.label1.TabIndex = 0;
this.label1.Text = "Oyun Bitti!..";
//
// label2
//
this.label2.Font = new System.Drawing.Font("Bookman Old Style", 15.75F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.label2.Location = new System.Drawing.Point(8, 48);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(112, 24);
this.label2.TabIndex = 1;
this.label2.Text = "Puaniniz:";
//
// OK
//
this.OK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.OK.Location = new System.Drawing.Point(24, 88);
this.OK.Name = "OK";
this.OK.TabIndex = 2;
this.OK.Text = "Baslat";
this.OK.Click += new System.EventHandler(this.button1_Click);
//
// button2
//
this.button2.Location = new System.Drawing.Point(136, 88);
this.button2.Name = "button2";
this.button2.TabIndex = 3;
this.button2.Text = "Yeter";
this.button2.Click += new System.EventHandler(this.button2_Click);
//
// label3
//
this.label3.Font = new System.Drawing.Font("Bookman Old Style", 15.75F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.label3.Location = new System.Drawing.Point(120, 48);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(56, 24);
this.label3.TabIndex = 4;
this.label3.Click += new System.EventHandler(this.label3_Click);
//
// Form3
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.Color.SkyBlue;
this.ClientSize = new System.Drawing.Size(234, 122);
this.ControlBox = false;
this.Controls.Add(this.label3);
this.Controls.Add(this.button2);
this.Controls.Add(this.OK);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form3";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Load += new System.EventHandler(this.Form3_Load);
this.ResumeLayout(false);
}
#endregion
private void Form3_Load(object sender, System.EventArgs e)
{
this.label3.Text = ppuan.ToString();
}
private void button2_Click(object sender, System.EventArgs e)
{
Application.Exit();
}
}
}
o Yaninca gösterilen form
Form2.cs:
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
namespace alleyway
{
///
/// Summary description for Form2.
///
public class Form2 : System.Windows.Forms.Form
{
int hhak;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button button1;
private System.Windows.Forms.Label label1;
///
/// Required designer variable.
///
private System.ComponentModel.Container components = null;
public Form2(int hak)
{
//
// Required for Windows Form Designer support
//
hhak=hak;
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
///
/// Clean up any resources being used.
///
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
///
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
///
private void InitializeComponent()
{
this.label2 = new System.Windows.Forms.Label();
this.button1 = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label2
//
this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 16F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.label2.Location = new System.Drawing.Point(176, 32);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(40, 40);
this.label2.TabIndex = 5;
//
// button1
//
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F,
System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.button1.Location = new System.Drawing.Point(232, 16);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(88, 40);
this.button1.TabIndex = 4;
this.button1.Text = "Devam";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// label1
//
this.label1.Font = new System.Drawing.Font("Comic Sans MS", 16F,
System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point,
((System.Byte)(0)));
this.label1.Location = new System.Drawing.Point(24, 0);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(136, 80);
this.label1.TabIndex = 3;
this.label1.Text = "Yandiniz!.. Kalan Hak =";
//
// Form2
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.BackColor = System.Drawing.Color.SkyBlue;
this.ClientSize = new System.Drawing.Size(330, 79);
this.ControlBox = false;
this.Controls.Add(this.label2);
this.Controls.Add(this.button1);
this.Controls.Add(this.label1);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Form2";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Load += new System.EventHandler(this.Form2_Load);
this.ResumeLayout(false);
}
#endregion
private void Form2_Load(object sender, System.EventArgs e)
{
label2.Text=hhak.ToString();}
private void button1_Click(object sender, System.EventArgs e)
{
this.Close();
}
}
}
C# ile Yazıcı Çıktısı Alma Đslemleri
C# ile Windows is uygulaması gelistiriyorsanız programınızın mutlaka yazıcı çıktısı
alma bölümü olacaktır. Bu makalede C# ile nasıl yazıcı çıktısı alınabileceğinin
temelleri üzerinde duracağım.
.NET sınıf kütüphanesi her alanda olduğu gibi yazıcı çıktısı alma ile ilgili bir takım sınıflar
sağlamıstır. PrintDocument sınıfı yazı çıktısı alma ile ilgili en temel sınıftır. Bu yazıda bu
sınıfın özelliklerini, olaylarını ve metotlarını ayrıntılı bir sekilde inceleyip tek sayfalı yada
çok sayfalı yazıcı çıktısının nasıl alınabileceğini göstereceğim. Ayrıca yazıcı çıktısı alma ile
çok yakından ilgili olan PrintPreview, PageSetupDialog ve PrintDialog gibi sınıflarıda
inceleyeceğiz.
PrintDocument Sınıfı
Bu sınıf programlarımıza yazıcı çıktısı alma desteğini eklemek için kullanabileceğimiz en
temel yapıdır. Bu sınıf türünden bir nesne yaratıldığında çıktı alma ile ilgili hemen her tür
bilgiye erismemiz mümkündür.
PrintDocument YaziciCiktisi = new PrintDocument();
seklinde bir tanımlama yaptığımızda varsayılan yazıcı(default printer) ile çalısılmaktadır.
Bir dökümanı yazıcıya göndermek için PrintDocument sınıfının Print() metodu kullanılır.
Print() metodu çağrıldığı anda PrintPage olayı meydana gelir. Bu olayı yakalayan kontrol
yazıcıya gönderilecek döküman üzerinde islemler yaparak çıktının seklini belirlemelidir.
Her bir sayfa için ayrıca PrintPage olayı meydana geleceği için her bir olay içinde doğru
sayfaları yazıcıya göndermek için bir takım islemler yapmak gerekecektir. Aksi halde her
defasında birinci sayfayı yazıcıya gönderme ihtimalimiz vardır. Kısacası PrintPage olayı
olmadan yazıcıya çıktı bilgilerini gönderemeyiz. Bu yüzden ilk olarak PrintPage olayını ve
bu olaya ait argümanları içeren PrintPageEventArgs sınıfını inceleyelim.
Önce PrintPage olayının argümanlarını içeren PrintPageEventArgs sınıfının üye
elemanlarını inceleyelim, ardında bir konsol uygulamasından yazıcıya nasıl bir döküman
göndereceğimizi göstereceğim.
PrintPageEventArgs sınıfnın üye elemanları :
Graphics : Yazıcıya gönderilecek döküman bilgilerini belirleyen grafik nesnesidir. Yazıcya
gönderilecek bilgilerin tamamı bu nesne içerisinde belirtilecektir. Not : Graphics sınıf
GDI+ kütüphanesinin en önemli sınıfıdr.
Cancel : Çıktı alma isleminin iptal edilip edilemeyeceği ile ilgili bilgi veren bool türünden
bir elemandır. Eğer değeri true ise çıktı alma islemi iptal edilecektir.
HasMorePages : Yazıcıya gönderilecek çıktının birden fazla sayfa kapladığı durumlarda
PrintPage olayına iliskin metotta bu özelliğin true olarak değistirilmesi gerekir. Böylece
bundan sonraki PrintPage olaylarında bu değisken kontrol edilerek diğer sayfaların çıktıya
gönderilmesi ile ilgili islemler yapılır.
MarginBounds : Yazıcıya gönderilen çıktı dökümanının en ve boyutlarını temsil eden
Rectangle türünden bir özelliktir. Rectangle sınıfıda GDI+ kütüphanesinin bir parçasıdır.
Bu özellikte yazıcıya gönderilecek çıktının sadece üzerine çizim yapılabilen kısmı belirtilir.
PageBounds : Yazıcıya gönderilen dökümanın tamamının en ve boy değerlerini tutan
yine Rectangle sınıfı türünden bir elemandır.
PageSettings: Đlgili dökümana ait sayfa ayarlarını tutan ve PageSettings sınıfı
türünden bir elemandır. PageSettings sınıfının Color, Landscape, Margins, PaperSize,
PaperSource, PrinterResolution gibi sayfa ile ilgili bilgi tutan üye özellikleri bulunmaktadır.
Simdi basit bir örnekle yazıcıya çıktı gönderelim. Örneğimizde varsayılan yazıcınıza, sol
üst kösesi (20,20) koordinatlarında eni ve boyu 100 olan bir dörtgen içeren sayfayı
göndereceğiz. Gönderilecek sayfadaki dörtgeni çizmek için tahmin edeceğiniz üzere
Graphics nesnesini kullanacağız.
using System;
using System.Drawing.Printing;
using System.Drawing;
class Printer
{
static void Main()
{
PrintDocument PD = new PrintDocument();
PD.PrintPage += new PrintPageEventHandler(OnPrintDocument);
try
{
PD.Print();
}
catch
{
Console.WriteLine("Yazıcı çıktısı alınamıyor...");
}
finally
{
PD.Dispose();
}
}
private static void OnPrintDocument(object sender, PrintPageEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Red,20,20,100,100);
}
}
Yukarıdaki programı derleyip çalıstırdığınızda hiç bir uyarı eğer verilmeden sisteminize bir
yazıcı bağlı OnPrintDocument() metodunda hazırlanan içerik yazıcıya gönderilecektir.
Eğer sisteminize bağlı bir yazıcı yoksa doğal olarak catch bloğundaki kod çalısacaktır.
Çizilen dörtgen nesnesinin kağıdın neresine basılacağını biz belirliyoruz. MarginBounds
özelliğini kullanarak çizilecek içeriğin doğru noktaya çizilmesini sağlayabiliriz. Bu özellik
sizin yazıcı ayarlarınız ile ilgili olduğu için programlama yolu ile kod içerisinden
değistirilemez. Yani bu özellik "read only" bir özelliktir. Dikkat edilmesi gereken diğer bir
noktada yazıcıya gönderilecek içeriğin PageBounds özelliği ile belirtilen dörtgenin dısına
tasmamasıdır. Bu yüzden çizimleri yapılırken bu özellik baz alınmalıdır.
Yukarıda yazdığımız basit programda eksiklik bulunmaktadır. Bu eksiklik çizilecek
dörtgenin tek bir sayfaya sığmadığı durumlarda görülür. Söz gelimi eğer dörtgenin
yüksekliğini 2000 yaparsak yazıcıdan sadece ilk kağıda sığan bölümü çıkacaktır. Birden
fazla sayfası olan çıktıları yazıcıya göndermek için PrintPageEventArgs sınıfnın
HasMorePages özelliği kullanılır. Bu özellik OnPrintDocument() metodu içerisinde true
değerine çekilerek çıktı alma isleminin devam ettiği belirtilmelidir. Ayrıca her bir sayfanın
içeriğide metot her çağrıldığında farklı bir biçimde olusturulacağı için programcının bu
ayrımı da kodlaması gerekmektedir. Örneğin yüksekliği 2000 pixel olan bir dikdörtgeni
tek sayfada bastıramayacağımız için ilk sayfaya sığmayan diğer bölümleri parçalayarak
her bir sayfaya sığacak sekilde ayarlamalıyız. Bu islem için PrintPageEventArgs sınıfnın
HasMorePages değiskenini kullanacağız.
Hemen diğer bölümlere geçmeden önce birden fazla sayfalı yazıcı çıktısı alma islemine
örnek verelim. Bu örnekte bir text dosyasının içeriğini yazıcıya nasıl gönderebileceğimizi
inceleyeceğiz. Tabi burda yazının birden fazla sayfada olup olmadığının kontrolünü
yapmamız gerekir. Yazıları yazıcı çıktısına göndermek için Graphics sınıfnın DrawString
metodunu kullanacağız. Bu metot grafik arayüzüne belirli bir fontta ve font büyüklüğünde
yazı yazmamızı sağlar. Önce örneği inceleyelim ardından örnek üzerinde biraz
konusacağız.
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Printing;
class Printer
{
private static StreamReader dosyaAkimi;
static void Main(string[] args)
{
dosyaAkimi = new System.IO.StreamReader("C:\\Print.txt");
PrintDocument PD = new PrintDocument();
PD.PrintPage += new PrintPageEventHandler(OnPrintDocument);
try
{
PD.Print();
}
catch
{
Console.WriteLine("Yazici çiktisi alinamiyor...");
}
finally
{
PD.Dispose();
}
}
public static void OnPrintDocument(object sender,PrintPageEventArgs e)
{
Font font = new Font("Verdana", 11) ;
float yPozisyon = 0 ; int LineCount = 0 ;
float leftMargin = e.MarginBounds.Left;
float topMargin = e.MarginBounds.Top;
string line=null;
float SayfaBasinaDusenSatir = e.MarginBounds.Height / font.GetHeight() ;
while (((line=dosyaAkimi.ReadLine()) != null) && LineCount <
SayfaBasinaDusenSatir)
{
yPozisyon = topMargin + (LineCount * font.GetHeight(e.Graphics));
e.Graphics.DrawString (line, font, Brushes.Red, leftMargin,yPozisyon);
LineCount++;
}
if (line = = null)
e.HasMorePages = false ;
else
e.HasMorePages = true ;
}
}
Yukarıdaki program herhangi bir text formatındaki dosyayı yazıcıya gönderek çıktı
almanızı sağlayacaktır. Dosyanın içeriğini yazıcıya gönderirken çıktının ne sekilde olacağı
tamamen programlama yolu ile bizim tarafımızdan yapılmaktadır. Örneğin çıktının yazı
fontunu GDI+ kütüphanesinin bir sınıfı olan Font ile yazı renginide yine GDI+
kütüphanesinin Brushes sınıfının üye elemanları ile rahatlıkla değistirebiliriz.
Yukarıdaki örnek uygulamada en önemli kısım dosya içeriğinin yazıcıyı gönderilmesi
sırasında görülür. Dosya içeriğinin birden fazla sayıda sayfa içermesi durumunda dosya
akımından bir sayfaya sığacak kadar satır okunmalıdır. Eğer dosya akımının sonuna
gelinmediyse HasMorePages özelliği true yapılarak OnPrintDocument metodunun
yeniden çağrılması gerekir. Kaynak koddanda gördüğünüz üzere dosya akımından okunan
satır null değereesit olduğunda yani dosyanın sonuna gelindiğinde HasMorePages özeliiği
false yapılarak Print() metodunun icrası sonlandırılmıstır.
Bir diğer önemli nokta ise yazıcıya gönderilecek her bir sayfada kaç satırın bulunacağının
belirlenmesidir. Sayfa basına düsen satır sayısı, sayfanın yazıcıya gönderilecek
bölümünün yüksekliğinin yani e.MarginBounds.Height 'in çıktıya gönderilecek yazıya
ait fontun yüksekliğine bölümü ile elde edilir. Sayfa basına üsen satır sayısı elde
edildikten sonra herbir sayfanın içeriği while döngüsü yardımı ile hazırlanır. Okunan satır
sayısı null değere esit olmayana kadar ve okunan satır sayısı sayfa basına düsen satır
sayısı olana kadar döngüye devam edilir. Döngü içerisinde PrintPageEventArgs olay
argümanlarını içeren sınıfın Graphics nesnesine DrawString() metodu yardımıyla dosya
akımından okunan satır yazılır. Bir sonraki satırın çıktı ekranının neresinden baslayacağını
tutmak için ise her bir satır okunduğunda yPozisyon'u kullanılan font'un yüksekliği kadar
artırılır. Bütün bu islemleri yaptıktan sonra HasMorePages özelliği ayarlanır ki sonraki
sayfalar çıktıya gönderilsin. Eğer dosya sonuna gelinmisse artık basılacak sayfa yok
demektir ve HasMorePages özelliği false olarak belirlenir.
Not : Dosya akımının neden OnPrintDocument() metodunun içinde tanımlanmadığını
merak ediyor olabilirsiniz. Bunun sebebi OnPrintDocument() metodunun her bir sayfa için
yeniden çağrılmasıdır. Eğer dosya akımını bahsi geçen metotta tanımlamıs olsaydık her
defasında dosya akımı bastan okunacağı için hiç bir zaman dosya akımının sonuna
gelemeyecektik ve her defasında sonsuza dek ilk sayfayı çıktıya göndermis olacaktık. Bu
yüzden dosya akımını global düzey diyebileceğimiz bir noktada yani ana sınıfımın bir üye
elamanı olacak sekilde tanımladık.
Aklınıza takılmıs olabilecek diğer bir nokta ise yazıcının renk ayarlarıdır. Eğer yazcınız
renkli çıktı almayı desteklemiyorsa DrawString() metoduna parametre olarak geçtiğimiz
Brushes.Red parametresinin bir önemi olmayacaktır. Bu yüzden dökümanları yazıcya
göndermeden yazcının renkli baskıyı desteleyip desteklemediğini kontrol etmek en akıllıca
yöntemdir. Bu sekildeki bri kontrol için PrintDocument sınıfının PrinterSettings özelliği
kullanılabilir. Bu özellik varsayılan yazcınız ile ilgili bir takım ayarları yapısında barındıran
özelliklere sahiptir. Örneğin varsayılan yazıcınızın renkli bakıyı destekleyip
desteklemediğini kontrol etmek için SupportsColor özelliğini asağıdaki gibi
kullanabilirsiniz. Not : SupportsColor özelliği bool türündendir.
using System;
using System.Drawing.Printing;
using System.Drawing;
class Printer
{
static void Main()
{
PrintDocument PD = new PrintDocument();
PD.PrintPage += new PrintPageEventHandler(OnPrintDocument);
if ( PD.PrinterSettings.SupportsColor )
{
//renkli baskı ayarları
}
else
{
//renksiz baskı ayarları
}
}
private static void OnPrintDocument(object sender, PrintPageEventArgs e)
{
....
}
}
PrinterSettings yolu ile elde edebileceğimiz diğer önemli özellikler asağıda listelenmistir.
CanDuplex : bool türünden olan bu değisken yazıcının arkalı önlü çıktı almayı
destekleyip desteklemediğini belirtir.
Copies: short türünden olan bu değisken yazıcıya gönderilecek dökümanın kaç kopya
çıkarılacğını belirtir. Eğer 10 sayfalık bir döküman için bu özelliği 5 olarak girerseniz 50
adet kağıdınızı yazıcıya yerlestirmeyi unutmayın.
CanDuplex : bool türünden olan bu değisken yazıcının arkalı önlü çıktı almayı
destekleyip desteklemediğini belirtir.
Duplex : Duplex enum sabiti türünden olan bu değisken arkalı önlü baskı özelliğini
belirler. Duplex numaralandırması Default,Sizmplex,Horizontal ve Vertical olmak üzere
dört tane üyesi vardır.
IsDefaultPrinter : PrinterName ile belirtilen yazıcının bilgisayarınızdaki varsayılan
yazıcı(default printer) olup olmadığını belirtir.
IsValid : PrinterName ile belirtilenin gerçekten sisteminize ait bir yazıcı olup olmadığını
belirtir.
PaperSizes : Yazıcı tarafından desteklenen sayfa ebatlarının PaperSizeCollection
türünden bir koleksiyon nesnesi ile geri döner. Bu koleksiyondaki her bir eleman
System.Drawing isim alanında bulunan PaperSize türündendir. PaperSize sınıfnın
Width(sayfa eni), Height(sayfa boyu),Kind(sayfa türü) gibi özellikleri bulunmaktadır.
PaperSources : Yazıcı tarafından desteklenen sayfa kağıt alma
kaynaklarını PaperSourceCollection türünden bir koleksiyon nesnesi ile geri döner. Bu
koleksiyondaki her bir eleman System.Drawing isim alanında bulunan PaperSource
türündendir. PaperSource sınıfnın Kind özelliği PaperSourceKind numaralandırması
türünden bir nesne olup kağıt kaynağının tipini belirtir.Bu numaralandırmanın bazı
semboleri sunlardır : Envelope, Cassette, Custom, Manuel, TractorFeed.
PrintToFile : Çıktının herhangi port yerine bir dosyaya yazdırılıp yazdırılmayacağını tutan
bool türünden bir değisken. Bu değisken daha çok birazdan göreceğimiz PrintDialog
ekranının görüntülenmesi sırasında değistirilip kullanılır.
Çıktı Ön-Đzleme Penceresi
Profesyonel uygulamaların tamamında yazıcıya çıktı göndermeden önce kullanıcıya ön
izleme imkanı sağlanır. .NEt ortamında program gelistiriyorsanız Windows'un standart ön
izleme penceresini programlama yolu ile görüntülemeniz son derece kolaydır. Bu ekranın
görüntülenmesi için System.Drawing isim alanında bulunan PrintPreviewDialog sınıfı
kullanılır. Bu sınıf ile iliskilendirilmis PrintDocument nesnesinin PrintPage olayına iliskin
metot çalıstırılarak ön-izleme penceresindeki içerik elde edilir.
Bir PrintPreviewDialog nesnesi olusturulduktan sonra nesnenin Document özelliğine
PrintDocument türünden bir nesne atanır. Ve ardından PrintPreviewControl türünden olan
nesne üzerinden Show() yada ShowDialog() metotları kullanılarak ön izleme ekranı
gösterilir.
Ön izleme çıktısnın görüntülendiği pencereyi elbette PrintDocument sınıfnın Print()
metodunu çağırmadan önce göstermeliyiz. Daha önce yaptığımız ve dosya içeriğini
yazıcıya gönderen uygulmanın Main() metodunu asağıdaki gibi değistirerek ön izleme
ekranından çıktıya gönderilecek içeriği görüntüleyelim.
static void Main(string[] args)
{
dosyaAkimi = new System.IO.StreamReader("C:\\Print.txt");
PrintDocument PD = new PrintDocument();
PD.PrintPage += new PrintPageEventHandler(OnPrintDocument);
PrintPreviewDialog pdlg = new PrintPreviewDialog();
pdlg.Document = PD;
pdlg.ShowDialog();
try
{
PD.Print();
}
catch
{
Console.WriteLine("Yazici çiktisi alinamiyor...");
}
finally
{
PD.Dispose();
}
}
Programı yeni haliyle derleyip çalıstırdığımızda ilk önce öıktı ön izleme ekranı asağıdaki
gibi gösterilecektir. Not : Çıktıya gönderilecek dosyanın yolu örneğimiz için "C:\Print.txt"
seklinde olmalıdır.
Sayfa Düzenleme Ekranı (PageSetupDialog Sınıfı)
Dökümanı çıktıya göndermeden önce gönderme isleminin hangi yazıcı ayarları ile
yapılacağını belirlemek için genellikle sayfa düzenleme ekranı gösterilir. Bu ekranda kağıt
tipinden, yazcının kağıt kaynağına kadar bir çok özelliği değistirmeniz mümkündür. Bu
ekranda yapılan bütün değisiklikler PrintDocument sınıfının PrinterSettings özelliğine
aktarılır. Sayfa düzenleme ekrana System.Drawing isim alanında bulunan
PrintSetupDialog sınıfı ile gerçeklestirilir. Bu sınıfının kullanımı PrintPreviewDialog
sınıfının kullanımı ile nerdeyse aynıdır. Bu yüzden ayrıca açıklmaya gerek duymuyorum.
Son olarak yazıcı ön izleme ekranından önce sayfa düzenleme ekranının gösterilmesini
sağlamak için uygulamamaızın Main() metodunu asağıdaki gibi değistirin ve çalıstırın.
static void Main(string[] args)
{
dosyaAkimi = new System.IO.StreamReader("C:\\Print.txt");
PrintDocument PD = new PrintDocument();
PD.PrintPage += new PrintPageEventHandler(OnPrintDocument);
PrintDialog pdiyalog = new PrintDialog();
pdiyalog.Document = PD;
pdiyalog.ShowDialog();
PrintPreviewDialog pdlg = new PrintPreviewDialog();
pdlg.Document = PD;
pdlg.ShowDialog();
try
{
PD.Print();
}
catch
{
Console.WriteLine("Yazici çiktisi alinamiyor...");
}
finally
{
PD.Dispose();
}
}
Programı derleyip çalıstırdığınızda karsınıza ilk çıkacak görüntü asağıdaki ekran olacaktır.
Bu örnekle birlikte yazıcı çıktısı alma ile ilgili temel islemlerin anlatıldığı yazımızın sonuna
geldik. .NET teki yazıcı çıktısı alma islemleri bu anlattıklarımla sınırlı değildir. Ancak bu
yazıda anlatılanlar bu konuya çok hızlı bir giris yapmanızı sağlamıstır. Đlerleyen yazılarda
görüsmek dileğiyle.
Kaynaklar :
MSDN Yardım Dökümanları
Düzenli Đfadeler(Regular Expressions) Nedir?
Regular expression bir metni düzenlemek yada metin içerisinden belli kurallara uyan alt
metinler elde etmek için kullandığımız bir dildir. Bir regular expression, string tipindeki
karakter topluluğuna uygulanır. Sonuç olarak substringler olusur yada orjinal metnin bir
kısmını içeren değistirilmis yeni metinler elde edilir.
Regular Expression’larda Kullanılan Özel Karakterler ve Etkileri
Regular expression desenleri tanımlamada kullanılan özel karakterleri örnekleri ile
anlatırsak sanırım regular expressionlar daha tanıdı ve kolay gelebilir.
1.) “.” Karakteri
Tek bir karakteri temsil eder(yeni satır karakteri hariç).
“CSharp.edir” seklindeki bir desen CSharpnedir, CSharpNedir, CSharpSedir, CSharp3edir
gibi stringleri döndürebilir.
2.) “[]” Karakterleri
Bir arrayi yada aralığı temsil eder.
“CSharp[SNY]edir” deseni, CSharpSedir, CSharpNedir ve CSharpYedir stringlerini
döndürür.
“CSharp[a-z]edir” seklindeki kullanım aralık belirtmeye yarar.
“CSharp[0-9]edir” seklindeki kılanlım ise sayısal aralık belirtmeye yarar.
3.) “?” Karakteri
Kendinden önceki karakterin stringte olması yada olmamasını sağlar.
“CSharpn?edir” deseni CSharpedir yada CSharpnedir döndürür.
4.) “\” Karakteri
Kendinden sonraki özel karakterin stringe dahil edilmesini sağlar.
“CSharpnedir\?” deseni CSharpnedir? Stringini döndürür. (Eğer “\” karakterini
kullanmamıs olsaydık CSharpnedi yada CSharpnedir dönerdi.)
5.) “*” Karakteri
Kendinden önceki karakterin yada stringin hiç olmaması yada istediği sayıda olmasını
sağlar.
“CSharpnedir*” deseni, CSharpnedi, CSharpnedir, CSharpnedirr, CSharpnedirrr, ...
döndürür. “CSharp(nedir)*” deseni ise CSharp, CSharpnedir, CSharpnedirnedir, ...
döndürür.
6.) “{}” Karakterleri
Kendinden önce gelen karakterin belirtilen sayıda tekrar etmesini sağlar.
“C{4}Sharpnedir” deseni, CCCCSharpnedir stringini döndürür.
7.) “^” Karakteri
Satır basını ifade eder.
“^CSharpnedir” deseni, satır basında “CSharpnedir” stringi varsa bunu döndürür.
8.) “$” Karakteri
Satır sonunu ifade eder.
“CSharpnedir$” deseni, satır sonunda “CSharpnedir” stringi varsa bunu döndürür.
Basit Bir Tarih Deseni Yapalım
Simdi isin pratiğine gelelim ve adım adım tarih deseni olusturalım. Daha sonra ise
olusturduğumuz bu tarih desenini bir konsol programında kullanalım.
Tarih desenimiz bir string içerisindeki, GG/AA/YYYY formatlarındaki tarihleri yakalayacak
yapıda olsun.
Önce desenimizin GG yani tarihin gün belirtilen kısmını tanımlayalım :
“(0?[1-9])”
// 1, 2, .., 9, 01, 02, ..., 09 gibi yazılmıs günleri tanımlar.
“([12][0-9])”
// 10, 11, ..., 29 gibi yazılmıs günleri tanımlar.
“(3[01])”
// 30, 31 günlerini tanımlar.
// Bu üç tanımı OR (|) islemiyle
// birlestirirsek gün tanımını elde ederiz.
// Gün tanımı :
“((0?[1-9])|([12][0-9])|(3[01]))”
Simdi desenimizin AA yani tarihin ay belirtilen kısmını tanımlayalım :
“(0?[1-9])”
// 1, 2, .., 9, 01, 02, .., 09 gibi yazılmıs ayları tanımlar.
“(1[0-2])”
// 10, 11, 12 aylarını tanımlar.
// Bu iki tanımı OR islemiyle
// birlestirirsek ay tanımını elde ederiz.
// Ay Tanımı :
“((0?[1-9])|(1[0-2]))”
Simdi desenimizin YYYY yani tarihin yıl belirtilen kısmını tanımlayalım :
“([12][0-9][0-9][0-9])”
//1000 ile 2999 yılları arasındaki tüm yılları içerir.
Ve son olarak tanımladığımız gün, ay ve yıl desenlerini “/” ile birlestirelim :
“((0?[1-9])|([12][0-9])|(3[01]))(/)(0?[1-9]|1[0-2])(/)([12][0-9][0-9][0-9]))”
Basit Bir Test Programı Yazalım
Simdi elde ettiğimiz tarih desenini test edebileceğimiz basit bir konsol programı yazalım. Ek
bilgi olarak “?” seklindeki bir ifadeyi desenin önüne ilave ederseniz, bir desen grubu ifade
etmis olursunuz ve birkaç deseni aynı anda kontrol edebilirsiniz.
using System;
using System.Text.RegularExpressions;
class Test
{
public static void Main()
{
// Regular Expression için bir desen (pattern) tanımlıyoruz :
string tarihDeseni=@"(?((0?[1-9])|([12][0-9])|(3[01]))(/)(0?[1-
9]|1[0-2])(/)([12][0-9][0-9][0-9]))";
// Regular Expression'umuzu tanımlıyoruz :
Regex benimRegex=new Regex(tarihDeseni);
// Kullanıcıdan tarih içeren metni talep ediyoruz :
Console.WriteLine("Lütfen içinde tarih olan bir metin giriniz
:");
// Tarih arayacağımız metni konsoldan alıyoruz :
string metin=Console.ReadLine();
// Metin içerisindeki tarihleri (birden fazla olabilir)
Collection nesnesine atıyoruz :
MatchCollection benimMatchCollection=benimRegex.Matches(metin);
// Metin içindeki herbir tarihi ekrana yazdırıyoruz :
foreach(Match benimMatch in benimMatchCollection)
{
Console.WriteLine(benimMatch.Groups["tarih"]);
}
Console.Read();
}
}
Bir Web Sitesindeki E-Mail Adreslerini Yakalamak (Düzenli Đfadeler 2)
Gene su sıkıcı düzenli ifadeler değil mi? Bu makalede düzenli ifadelerin ne kadar
etkileyici olduğunu bir örnek üzerinde anlatmaya çalısacağım.
Web sitelerinin birebir kopyasını kendi bilgisayarınıza kopyalayan ( Teleport gibi)
programları bilirsiniz. Önce bir sayfanın kaynak kodunu indirir. Đçindeki linkleri ve resim
dosyalarını belirler. Sonra sıra bu link ve resim dosyalarına gelir.
Hiç elinizdeki kaynak kodun linklerini nasıl ayıklayacağınızı düsündünüz mü?
Đste düzenli ifadeler burada gerçekten harikalar yaratır.
Fazla uzatmadan örneğimize geçelim...
E-Mail Yakalayıcı
Simdi yapacağımız örnekte önce bir web sitesinin kaynak kodunu indirip, içerisindeki email
linklerini elde edeceğiz.
Öncelikle sayfanın kaynak kodunu indirelim
Bunun için iki basit metod yazdım. Đlki sayfanın adresini kullanıcıdan alıyor, ikincisi
sayfaya bağlanıp kaynak kodu indiriyor :
// Adresi Alan Metod :
private string AdresiAl()
{
string adres="http://" + txtAdres.Text;
return adres;
}
// Sayfanın Kaynak Kodunu Döndüren Metod :
private string KaynakAl(string adres)
{
lblStat.Text="Siteye Bağlanıyor...";
WebResponse benimResponse=null;
try
{
WebRequest benimWebRequest=WebRequest.Create(adres);
benimResponse=benimWebRequest.GetResponse();
}
// Eğer internet bağlantısı yoksa yada site adresi yanlıs ise :
catch(WebException e)
{
lblStat.Text="Siteye Bağlanamıyor.";
return null;
}
// Site içeriği stream olarak alınıyor :
Stream str=benimResponse.GetResponseStream();
StreamReader reader=new StreamReader(str);
string kaynak=reader.ReadToEnd();
// Tüm içerik küçük harfle döndürülüyor.
//Daha fazla kontrol yapmamak için bir önlem
return kaynak.ToLower();
}
Sayfanın kaynak kodunu indirdikten sonra içindeki e-mail linklerini bulup bir diziye atan
bir baska fonksiyon daha yazdım. Öncelikler e-mail linkini yakalayan deseni (pattern)
açıklamaya çalısalım.
Bildiğiniz gibi e-mail linkleri
<a href="mailto:aaa@bbb.ccc"> gibi ifade edilir.
O zaman desenimiz (href=) ifadesi ile baslamalıdır.
Ardından (') yada (") karakterleri gelebilir.
"(href=)((')|(""))"
Sonra "mailto:" ifadesi gelir :
"(href=)((')|(""))(mailto:)"
"mailto:" ifadesinden sonra istediğimiz ifade yani e-mail adresi gelir. Bunu "mail"
isminde bir grup tanımlayarak elde edeceğiz.
"(href=)((')|(""))(mailto:)(?<mail>(.*))"
// (.*) ifadesi kendinden sonra gelen desene kadar her karakteri alan bir desendir.
Simdi desenimizi sonlandıralım :
"(href=)((')|(""))(mailto:)(?<mail>(.*))((')|(""))"
Kısaca, "mailto:" ile tırnak karakterleri arasındaki her ifade bizim için mail grubuna
dahil oldu.
Simdi E-mail adreslerini dizi seklinde döndüren metodumuzu yazalım :
// Sayfanın içindeki mail adreslerini dizi seklinde döndüren metod :
private string[] MailAl(string kaynak)
{
lblStat.Text+= "Kaynak kod alındı... " + "Mailler ayrıstırılıyor... ";
// Desenimiz :
string mailDeseni=@"(href=)((')|(""))(mailto:)(?<mail>(.*))((')|(""))";
int i=0;
// Regular Expressionumuzu tanımlıyoruz :
Regex benimRegex=new Regex(mailDeseni);
Match str=benimRegex.Match(kaynak);
// Olusturduğumuz deseni sitenin kaynak kodunda karsılastırıyoruz :
MatchCollection mailCol=benimRegex.Matches(kaynak);
string[] mail=new string[mailCol.Count];
// Bulunan her e-mail adresini mail[] dizisine atıyoruz :
foreach(Match mailMatch in mailCol)
{
mail[i]=mailMatch.Groups["mail"].ToString();
i++;
}
return mail;
}
Simdi Yakala butonuna basılınca icra edilecek olay kodunu yazalım :
// Simdi e-mail yakalamak için bu yazdığımız metodları button_Click olayı ile
birlestirelim :
private void btnYakala_Click(object sender, System.EventArgs e)
{
lblStat.Text="";
if(txtAdres.Text=="")
{
MessageBox.Show("Lütfen Bir Adres Girin !");
}
else
{
// Sitenin Adresini alıyoruz :
string adres=AdresiAl();
// Sitenin Kaynak Kodunu alıyoruz :
string kaynak=KaynakAl(adres);
// E-Mail adreslerini alıyoruz :
if(kaynak!=null)
{
string[] mail=MailAl(kaynak);
lblStat.Text+="Đslem sona erdi." + mail.Length + " tane mail adresi
yakalandı.";
foreach(string yakalananMail in mail)
{
// Her e-mail adresi listbox'a giriliyor :
lbxEmail.Items.Add(yakalananMail);
}
}
}
}
Açıklama
Bazı sitelerde frameset kullanıldığından sayfada e-mail linki görülse bile programımız
bunları döndürememekte. Bu sayfaların framelerinin linkleri verilerek e-mail adresleri
elde edilebilir.
Yine bazı sitelerde linkler javascript kodu ile erisildiğinden bu adreslerde programımız
tarafından erisilememektedir.
C# ile Taskbarda Çalısan Program Hazırlamak
Bu makalemde size NotifyIcon ve ContextMenu kullanarak bir taskbara yerlesen program
nasıl yapılır, onu göstereceğim. Daha fazla uzatmadan hemen kodlarımızı yazmaya
baslayalım.
Đlk olarak Visual Studio'yu açalım ve yeni bir proje yaratalım. Bu projenin adına
istediğiniz gibi bir isim verebilirsiniz. Projemiz "C# Windows Application" olmalıdır.
Projemizi yarattıktan sonra Add / New Item diyerek yeni bir Icon ekleyelim. Iconumuzun
Build Action'ı mutlaka "Embedded Resource" olmalı. Daha sonra Form1'ın kod kısmına
gecelim.
Sınıfımızın içine
private NotifyIcon notifyicon;
private ContextMenu menu;
kodlarını ekleyelim. Formumuza iki kere tıklayalım ve asağıdaki metotları kaynak
kodumuza ekleyelim.
private void Form1_Load(object sender, System.EventArgs e)
{
notifyicon = new NotifyIcon(); //Yeni bir NotifyIcon tanımladık
notifyicon.Text = "NotifyIcon Ornegimiz"; //Mouse ile uzerine geldiğimizde olusacak
yazı
notifyicon.Visible = true; //Gorunur ozelligi
notifyicon.Icon = new Icon("Icon1.ico"); //Iconumuzu belirledik
menu = new ContextMenu(); //Yeni bir ContextMenu tanımladık
menu.MenuItems.Add(0, new MenuItem("Goster", new
System.EventHandler(Goster_Click))); //Menuye eklemeler yapıyoruz.
menu.MenuItems.Add(1, new MenuItem("Gizle", new
System.EventHandler(Gizle_Click)));
menu.MenuItems.Add(2, new MenuItem("Kapat", new
System.EventHandler(Kapat_Click)));
notifyicon.ContextMenu = menu; //Menumuzu notifyiconun menusu olarak tanımladık
}
protected void Goster_Click(object sender, System.EventArgs e)
{
Show(); //Formumuzu normal ebatlara getirecek
}
protected void Gizle_Click(object sender, System.EventArgs e)
{
Hide(); // Formumuzu minimize edecek
}
protected void Kapat_Click(object sender, System.EventArgs e)
{
Close(); //Formumuzu kapatacak
}
Evet, simdi programımızı çalıstırmaya hazırız. E o zaman çalıstıralım ve sonucu görelim.
Gorduğunuz gibi programımız çalıstı. Programı kapatalım. O da ne! Iconumuz hala
taskbarda duruyor. Peki bunu nasıl düzelteceğiz? Hemen cevap verelim. Kaynak
kodumuzun biraz üstlerine bakıyoruz ve su satırları goruyoruz:
protected override void Dispose( bool disposing )
{
if( disposing )
{
if(components ! null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
Bu satırları asağıdaki gibi değistirdiğimizde progrm kapatıldığında taskbar daki icon da
silinecektir.
protected override void Dispose( bool disposing )
{
if( disposing )
{
this.notifyicon.Dispose();;
components.Dispose();
}
base.Dispose( disposing );
}
Ve bir sorunumuz daha var. Programımızı açtığımız anda Form1 de gözuküyor. Peki
Form1 gözükmeden sadece Iconumuzun gözükmesini nasıl sağlayacağız? Bunun da
çozümü var. Biraz daha yukarılara bakıp
InitializeComponent();
satırından bir sonraki satıra su kodları koyuyoruz:
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false;
Bunları yaptıktan sonra yapmamız gerek bir değisiklik daha var. O da Goster_Click ve
Gizle_Click'i su sekilde değistirmek:
protected void Goster_Click(object sender, System.EventArgs e)
{
this.WindowState = FormWindowState.Normal; //Formumuzu normal ebatlara getircek
}
protected void Gizle_Click(object sender, System.EventArgs e)
{
this.WindowState = FormWindowState.Minimized; // Formumuzu minimize edecek
}
Ve simdi hersey tamam. Programımız artık calısmaya hazır durumda. Hemen calıstırıp
sonucu görebiliriz.
Umarım herkes için faydalı bir yazı olmustur. Benim için kod yazmak yazı yazmaktan
daha kolay, bunu herkesin tatmasını isterim :)) Yeni yazılarda gorusmek dileğiyle
hosçakalın.
"Singleton" Tasarım Deseninin(Pattern) C# ile Gerçeklestirilmesi
Yazılım mühendisliğinin sanatsal yönü ağır olan "design pattern" kavramını bir çoğumuz
mutlaka duymusuzdur, ama rutin islerimizden kendimizi bosa çıkarıp bir türlü inceleme
fırsatı bulamamısızdır, Bu ve bundan sonraki bir kaç makalelik dizide size
kendi terminolojik yapısı içinde deyimlesmis olan "design pattern" yani "desen tasarımı"
kavramını açıklamaya çalısacağım. Elbette açıklamalarımı en çok bilinen tasarım desenleri
ile destekleyeceğim. Bu konudaki ilk makalede en basit ve en popüler tasarım
desenlerinden biri olan "Singleton" deseninden bahsedip "pattern design" sanatına daha
farklı bir bakıs açısıyla yaklasmanızı sağlamaya çalısacağım. O halde ise basit tanımlarla
baslayalım.
"Design Pattern" Nedir ?
Bildiğiniz üzere günümüzde yazılım mühendisliği alanında en fazla ses getiren kurgu
yazılımın gerçek dünya ile olan iliskisinin sağlanabilmesidir. Bu iliski elbette nesne
yönelimli programlama tekniği ile sağlanmaktadır. Nesne yönelimli programlama tekniği
bilgisayar uygulamalarını soyut bir olgudan çıkararak insanların daha kolay
algılayabileceği hale getirmistir. Öyle görünüyorki, makro düzeyde gerçek hayatı
modelleme ile baslayan bu iliski mikro düzeydede büyük ses getirecektir. Nitekim son
yıllarda gelistirilen yapay sinir ağları ile makinelerin çalısma sistemlerinin gün geçtikçe
insanların veya canlıların çalısma sekline yaklastığı görülmektedir. Artık bilgisayarlardan
sadece verilen komutları yerine getirmek değil, belirli olaylar ve durumlar karsısında bazı
hükümlere varabilmeleride istenmektedir. Burada vurgulamak istediğim nokta sudur :
bizler yazılım mühendisleri veya programcılar olarak gerçek hayatı ne kadar iyi
modelleyebiliyorsak o kadar basarılı sayılırız. Peki "desgin pattern" konusu nerede
devreye girmektedir? Đste benimde gelmek istediğim nokta budur; "desgin patterns" bir
modelin tasarımın ustaca tasarlanmasını sağlayacak çesitli desenlerin olusturulmasını ve
bu desenlerin ihtiyaç dahilinde herhangi bir modelin insaasında kullanılabilmesini sağlar.
"Design pattern" kavramı bir kurallar topluluğundan ziyade bir isi nasıl ve en güzel ne
sekilde yapabileceğimiz gösteren yöntemler topluluğudur. Öyleki iyi bir yazılım
modelleyicisiyseniz kendi tasarım desenlerinizi olusturabilir ve bunları diğer ustaların
kullanımına sunabilirsiniz. Tasarım desenleri tecrübe ile olusturulan yapılardır.
Bazıları olmazsa olmaz yapılar olmasına rağmen bazıları tamamen yazılımın sanatsal
yönünü göstermek için tasarlanmıstır . Örneğin bu yazımın ana konusunu belirleyen
"Singleton" tasarım deseni yıllardan beri bir çok kisi tarafından kullanılmıstır. Sizde bu
yazıda bu desenin amacını ve nasıl uygulandığını öğrendiğinizde eminimki projelerinizde
mutlaka kullanacaksınız. Hemen sunuda belirteyimki bu tasarım deseni sizi uzaya
götürmeyecektir, bu yüzden beklentilerinizi biraz daha azaltmanızda fayda var.
O halde "design pattern" yada "tasarım deseni" ni su sekilde tanımlayabiliriz : Bir tasarım
problemini en basit ve en efektif bir sekilde çözüme kavusturacak yöntemdir.
"Design Pattern" Kaynakları
"Design Pattern" konusunda yazılmıs en güzel ve en popüler kaynaklardan biri Erich
Gamma, Richard Helm, Ralph Johnson ve John Vlissides tarafından kaleme alınmıs
"Design Patterns: Elements of Reusable Object-Oriented Software" kitabıdır. Bu kitapta
en popüler tasarım desenleri anlatılmıs ve bu desenlerin çesitli uygulamalarına yer
verilmistir. "Desgin pattern" guru'ları olarak anılan bu 4 kisi "Gangs Of Four(GOF)" olarak
ta bilinmektedir. Zaten bahsi geçen kitapta anlatılan tasarım desenlerine de genel olarak
GoF tasarım desenleri denilmektedir. Bu yazı ile baslayan yazı dizisinde GOF olarak anılan
tasarım desenlerini sizlere aktarıp çesitli kullanım alanlarını açıklayacağım.
GOF tasarım desenleri genel olarak 3 ana grup altında incelenir. Bu gruplar ve herbir
gruptaki tasarım desenlerinin isimleri asağıda verilmistir.
1 - Creatinal Patterns Bu desenler bir yada daha fazla nesnenin olusturulması ve
yönetilmesi ile ilgilidir. Örneğin bu yazıda anlatacağım ve bir uygulamanın ömrü boyunca
belirli bir nesneden sadece bir adet bulunmasını garantileyen Singleton deseni bu gruba
girmektedir. Bu gruptaki diğer desenler ise
• Abstract Factory
• Builder
• Factory Method
• Prototype
olarak bilinmektedir. Bu desenlerin bir çoğunu ilerleyen yazılarımda ele alacağım.
Simdilik sadece bir giris yapıyoruz.
2 - Behavioral Patterns Bu gruptaki desenlerin amacı belirli bir isi yerine getirmek için
çesitli sınıfların nasıl birlikte davranabileceğinin belirlenmesidir. Bu gruptaki desenler ise
asağıdaki gibidir.
• Chain of responsibility
• Command
• Interpreter
• Iterator
• Mediator
• Memento
• Observer
• State
• Strategy
• Template method
• Visitor
3 - Structural Patterns Bu gruptaki desenler ise çesitli nesnelerin birbirleri ile olan
iliskileri temel alınarak tasarlanmıstır. Bu gruptaki tasarım desenleri ise sunlardır:
• Adapter
• Bridge
• Composite
• Decorator
• Façade
• Flyweight
• Proxy
Bu giris bilgisinden sonra simdi nesnelerin yaratılması ile ilgili grup olan "Creatinal
Patterns" grubunda bulunan "Singleton" desenini açıklamaya basalayabiliriz.
Singleton Deseni
Singleton deseni bir programın yasam süresince belirli bir nesneden sadece bir
örneğinin(instance) olmasını garantiler. Aynı zamanda bu desen, yaratılan tek
nesneye ilgili sınıfın dısından global düzeyde mutlaka erisilmesini hedefler. Örneğin bir
veritabanı uyglaması gelistirdiğinizi düsünelim. Her programcı mutlaka belli bir anda
sadece bir bağlantı nesnesinin olmasını isteyecektir. Böylece her geretiğinde yeni bir
bağlantı nesnesi yaratmaktansa varolan bağlantı nesnesi kullanılarak sistem
kaynaklarının daha efektif bir sekilde harcanması sağlanır. Bu örnekleri dahada artırmak
mümkündür. Siz ne zaman belli bir anda ilgili sınıfın bir örneğine ihtiyaç duyarsanız bu
deseni kullanabilirsiniz.
Peki bu islemi nasıl yapacağız.? Nasıl olacakta bir sınıftan sadece ve sadece bir nesne
yaratılması garanti altına alınacak? Aslında biraz düsünürseniz cevabını hemen
bulabilirsiniz! Çözüm gerçekten de basit : statik üye elemanlarını kullanarak.
Singleton tasarım desenine geçmeden önce sınıflar ve nesneler ile ilgili temel bilgilerimizi
hatırlayalım. Hatırlayacağınız üzere bir sınıftan yeni bir nesne olusturmak için yapıcı
metot(constructor) kullanılır. Yapıcı metotlar C# dilinde new anahtar sözcüğü kullanılarak
asağıdaki gibi çağrılabilmektedir.
Sınıf nesne = new Sınıf();
Bu sekilde yeni bir nesne olusturmak için new anahtar sözcüğünün temsil ettiği
yapıcı metoduna dısarıdan erisimin olması gerekir. Yani yapıcı metodun public olarak
bildirilmis olması gerekir. Ancak "Singleton" desenine göre belirli bir anda sadece bir
nesne olabileceği için new anahtar sözcüğünün ilgili sınıf için yasaklanması gerekir yani
yapıcı metodun protected yada private olarak bildirilmesi gerekir. Eğer bir metodun
varsayılan yapıcı metodu(default constructor- parametresiz yapıcı metot) public olarak
bildirilmemisse ilgili sınıf türünden herhangi bir nesnenin sınıfın dısında tanımlanması
mümkün değildir. Ancak bizim isteğimiz yalnızca bir nesnenin yaratılması olduğuna göre
ilgili sınıfın içinde bir yerde nesnenin olusturulması gerekir. Bunu elbette statik bir
özellik(property) yada statik bir metotla yapacağız. Bu statik metot sınıfın kendi içinde
yaratılan nesneyi geri dönüs değeri olarak bize gönderecektir. Peki bu nesne nerde ve ne
zaman yaratılacaktır? Bu nesne statik metodun yada özelliğin içinde yaratılıp yine sınıfın
private olan elemanına atanır. Tekil olarak yaratılan bu nesne her istendiğinde eğer nesne
zaten yaratılmıssa bu private olan elemanın referasına geri dönmek yada nesneyi yaratıp
bu private değiskene atamak gerekmektedir. Sanırım bu deseni nasıl
uygulayabileceğimizi kafanızda biraz canlandırdınız. O halde daha fazla uzatmadan
desenimizi uygulamaya geçirelim.
Singleton Deseninin 1. versiyonu
public class SingletonDeseni
{
private static SingletonDeseni nesne = new SingletonDeseni();
private SingletonDeseni()
{
}
public static SingletonDeseni Nesne
{
get
{
return nesne;
}
}
}
Yukarıdaki sınıf örneğinde SingletonDeseni sınıfı belleğe yüklendiği anda statik olan
SingletonDeseni nesnesi yaratılacaktır. Bu nesne yaratılısının new anahtar sözcüğü ile
yapıldığına dikkat edin. Eğer siz Main() gibi bir metodun içinden bu nesneyi yaratmaya
kalksaydınız derleme asamasında hata alırdınız. Çünkü public olan herhangi bir yapıcı
metot bulunmamaktadır. Ayrıca
Siz Main() gibi bir metodun içinden yaratılan bu nesneye
SingletonDeseni nesne = SingletonDeseni.Nesne;
seklinde erismeniz mümkündür. Böylece yukarıdaki deyimi her kullandığınızda size geri
dönen nesne, sınfıın belleğe ilk yüklendiğinde yaratılan nesne olduğu garanti altına
alınmıs oldu. Dikkat etminiz gereken diğer bir nokta ise nesneyi geri döndüren özelliğin
yalnızca get bloğunun olmasıdır. Böylece bir kez yaratılan nesne harici bir kaynak
tarafından hiç bir sekilde değistirilemeyecektir.
Yukarıdaki SingletonDeseni sınıfını asağıdaki gibi de yazmamız mümkündür.
Singleton Deseninin 2. versiyonu
public class SingletonDeseni
{
private static SingletonDeseni nesne = new Singleton();
private SingletonDeseni()
{
}
public static Singleton Nesne()
{
return nesne;
}
}
Dikkat ederseniz iki sınıfın tek farkı olusturulan nesneye erisme biçimidir. Đlk versiyonda
nesneye özellik üzerinden erisilirken ikinci versiyonda metot üzerinden erisilmektedir.
Değismeyen tek nokta ise her iki erisim aracının da statik olmasıdır.
Yukarıdaki her iki versiyonda da biz yaratılan nesneyi
SingletonDeseni nesne = SingletonDeseni.Nesne;
yada
SingletonDeseni nesne = SingletonDeseni.Nesne();
seklinde istediğimizde nesne zaten yaratılmıs durumda olmaktadır. Oysa bu sınıfı daha
efektif bir hale getirerek yaratılacak nesnenin ancak biz onu istediğimizde yaratılmasını
sağlayabiliriz. Bu durumu uygulayan Singleton deseninin 3 versiyonunu olarak asağıda
görebilirsiniz.
Singleton Deseninin 3. versiyonu
public class SingletonDeseni
{
private static SingletonDeseni nesne;
private SingletonDeseni()
{
}
public static Singleton Nesne()
{
if(nesne == null)
nesne = new SingletonDeseni();
return nesne;
}
}
Gördüğünüz üzere nesne ilk olarak sınıf belleğe yüklendiğinde değilde o nesneyi ilk defa
kullanmak istediğimizde yaratılıyor. Đlgili nesneyi her istediğimizde yeni bir nesnenin
yaratılmaması içinde
if(nesne == null)
seklinde bir kosul altında nesnenin yaratıldığına dikkat edin.
Not : 3.versiyonda nesneyi yaratan bir metot olabileceği gibi 1. versiyondaki gibi sadece
get bloğu olan özellikte olabilir.
Herseye rağmen yukarıdaki 3 versiyonda bazı durumlar için tek bir nesnenin olusmasını
garanti etmemis olabilirz. Eğer çok kanallı(multi-thread) bir uygulama gelistiriyoırsanız
farklı kanalların aynı nesneyi tekrar yaratması olasıdır. Ancak eğer çok kanallı
çalısmıyorsanız(çoğunlukla tek thread ile çalısırız) yukarıdaki sade ama öz olan 3
versiyondan birini kullanabilirsiniz. Ama eğer çok kanallı programlama modeli söz konusu
ise ne yazıkki farklı kanalların aynı nesneden tekrar yaratmasını engelemek için ekstra
kontroller yapmanız gerekmektedir. Ne yazıkki diyorum çünkü bu yapacağımız kontrol
performansı büyük ölçüde düsürmektedir.
O halde çok kanallı uygulamalarda kullanabileceğimiz Singleton desenini yazalım.
Singleton Deseninin 4. versiyonu
public class SingletonDeseni
{
private static SingletonDeseni nesne;
private static Object kanalKontrol = new Object;
private SingletonDeseni()
{
}
public static Singleton Nesne()
{
if(nesne == null)
{
lock(kanalKontrol)
{
if(nesne == null)
{
nesne = new SingletonDeseni();
}
}
}
return nesne;
}
}
Yukarıdaki desendeki püf nokta lock anahtar sözcüğünün kullanımıdır.Eğer nesne ilk defa
yaratılcaksa yani daha önceden nesne null değere sahipse lock anahtar sözcüğü ile
isaretlenen blok kitlenerek baska kanalların bu bloğa erismesi engellenir.
Böylece kilitleme islemi bittiğinde nesne yaratılmıs olacağı için, kilidin kalkmasını
bekleyen diğer kanal lock bloğuna girmis olsa bile bu bloktaki ikinci if kontrolü nesnenin
yeniden olusturulmasını engelleyecektir. Böylece çok kanallı uygulamalar içinde tek bir
nesnenin olusmasını ve bu nesneye erisimi garanti altına alan Singleton desenini
tasarlamıs olduk.
Son olarak lock anahtar sözcüğünü kullanmadan çok kanallı uygulamalar içinde tek bir
nesneyi garanti altına alacak deseni yazalım. Asağıda Singleton desenin 5. versiyonu
bulunmaktadır.
Singleton Deseninin 5. versiyonu
public class SingletonDeseni
{
private static SingletonDeseni nesne = new SingletonDeseni ();
private static SingletonDeseni()
{
}
private SingletonDeseni()
{
}
public static SingletonDeseni Nesne
{
get
{
return nesne;
}
}
}
Bu versiyonun birinci versiyondan tek farkı yapıcı metodunda statik olmasıdır. C# dilinde
statik yapıcı metotlar bir uygulama domeninde ancak ve ancak bir nesne yaratıldığında
yada statik bir üye eleman referans edildiğinde bir defaya mahsus olmak üzere çalıstırılır.
Yani yukarıdaki versiyonda farklı kanalların(thread) birden fazla SingletonDeseni nesnesi
yaratması imkansızdır. Çünkü static üye elemanlar ancak ve ancak bir defa çalıstırılır.
Son versyion basit ve kullanıslı görünmesine rağmen kullanımının bazı sakıncaları vardır.
Örneğin Nesne Özelliği dısında herhangi bir statik üye elemanınız var ise ve ilk olarak bu
statik üye elemanını kullanıyorsanız siz istemedğiniz halde SingletonDeseni nesnesi
yaratılacaktır. Zira yukarıda da dediğimz gibi bir statik yapıcı metot herhangi bir statik
üye elemanı kullanıldığı anda çalıstırılır. Diğer bir sakıncalı durumda birbirini çağıran
statik yapıcı metotların çağrılması sırasında çeliskilerin olusabileceğidir. Örneğin her
static yapıcı metot ancak ve ancak bir defa çalıstırılır dedik. Eğer çalıstırılan bir static
metot diğer bir statik metodu çağırıyor ve bu statik metotta ilkini çağırıyorsa bir çeliski
olacaktır.
Kısacası eğer kodunuzun çeliski yaratmayacağından eminseniz 5. deseni kullanmanız
doğru olacaktır. Eğer çok kanallı uygulama gelistiriyorsanız 4. versiyonu, çok kanallı
uygulama gelistirmiyorsanızda 3. versiyonu kullanmanız tavsiye edilmektedir.
Singleton deseni konulu makalenin sonuna gelmis bulunmaktayız. Eğer ileride bir gün
yukarıdaki desenlerin birini kullanma ihtiyacı hissederseniz hangi deseni ne amaçla
kullandığınızı bizimle paylasırsanız seviniriz.
Bir diğer "Creational Patterns" deseni olan "Abstract Factory" desenini anlatacağım yazıda
görüsmek üzere.
XP Stilinde Kontroller ile Çalısma
Bu yazımızda Windows Form kontrollerinin veya nesnelerinin Windows XP stili
görünümlerini nasıl elde edebileceğimizi göreceğiz.
Microsoft Framework v1.1’ de bu özellik henüz pratik bir sekilde yok. Bu yüzden yolumuz
biraz uzun.
Elde edeceğimiz bu görünüm Windows Xp’ den önceki isletim sisteminde haliyle
görünmeyecek, o isletim sisteminin default haliyle görünecektir(mesela butonlar önceki
isletim sistemlerinde gri renkli görünüyordu).
Herhangi bir karısıklık çıkmaması için yönergeleri beraber takib edelim.
Hemen islem adımlarımıza baslayalım:
Microsoft Visual Studio.NET’ i açın.
File/New/Project’ i tıklayın.
Açılan Pencerede Project Type alanında Visual C# Project seçili olsun.
Aynı pencerede Templates alanında Windows Application seçili olsun.
Aynı pencerede Name alanına XPStyle yazın.
Aynı pencerede Location alanında mevcut yolun sonundaki klasör isminide XPStyle
yapın.
Projenin açılması için Okey butonuna tıklayın.
Simdi Form1.cs[Design] görünümüne sahipsiniz.
Formumuza ; Button, radioButton, checkBox, textBox, progressBar ve trackBar
ekleyin.
Button, radioButton, ve checkBox nesenelerinin Properties penceresinde Flat Style
kısmını System yapın.
Düğerleri için bunu yapmaya gerek yok.
Menüden File/Save All tıklayın ve Menuden Build/Build Solutin tıklayın.
Elimizde mevcut bir görünüm olustu, Projeyi bu haliyle çalıstırırsanız(Debug/Start),
Form elemanlarında XP Stilini göremeyeceksiniz,
Mesela Buton hala asağıdaki gibi gri renkte görünecek.
Simdi XP Stil görünümünü elde etmek için yönergeleri izleyin:
Menüden, Project/Add Class tıklayın.
Açılan pencerede Templates kısmında XML File(herhangi bir .cs dosyası da olabilir)
seçin.
Aynı pencerede Name kısmındaki alanı tamamen temizleyin.
“[Proje Adı].exe.manifest” yazım biçiminde XPStyle.exe.manifest yazın.
Bu projenin adını XPStyle olarak belirlemistik
Dosyamızın olusması için OK butonuna tıklayın.
Olusturduğumuz XPStyle.exe.manifest adlı dosyayı açın ve içine asağıdaki kodları
yapıstırın.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" processorArchitecture="X86"
name="Microsoft.Winweb.<Executable Name>" type="win32"/>
<description>.NET control deployment tool</description><dependency>
<dependentAssembly>
<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls"
version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
</assembly>
NOT: “<” “>” karakterlerinin < ve > haline dönüsmesi söz konusu olabilir. Bu yüzden bu
kodları önce bir NotePad’ e yapıstırın sonra buradan Select All deyip tekrar kopyalayın ve
XPStyle.exe.manifest dosyamıza yapıstırın.
Bu kodda <Executable Name> kısmına Projemizin adı olan XPStyle yazın
Yeni hal: name="Microsoft.Winweb.XPStyle" seklinde olacak.
Menüden File/Save All tıklayın ve Menuden Build/Build Solutin tıklayın.
Microsoft Visual Studio.NET’ i indirin ve projenizin bulunduğu klasöre geçin
Bu klasörün içinde XPStyle.exe.manifest dosyasını göreceksiniz.
Bu dosyayı kopyalayıp, Obj klasörünün içine girin, buradan da Debug klasörüne tıklayıp
içine girin ve dosyayı buraya yapıstırın.
Çalısma esnasında faydalanmak içi bu dosyayı bin\debug klasörüne de kopyalayabilirsiniz
Microsoft Visual Studio.NET’ i açın
Menüden, File/Open/File tıklayın.
Açılan pencerede Obj\Debug klasörüne ulasın.
Buradan XPStyle çalıstırılabilir dosyanızı seçin ve Open butonuna tıklayın.
Açılan XPStyle.exe dosyasının içindeyken sağ tıklayın.
Açılan menuden Add Resource tıklayın.
Açılan pencereden import butonuna tıklayın.
Açılan pencreden Files of type alanında All Files seçin.
Görünen dosyalardan XPStyle.exe.manifest dosyasını seçip Open butonuna tıklayın.
Açılan Custom Resource Type penceresinde Resource Type alanına “RT_MANIFEST” yazın
ve Okey butonuna tıklayın.
XPStyle.exe(101-Data) dosyası açıldı. Bu dosyadayken Properties penceresinden ID
alanının 101 olan değerini 1 yapın
Menüden File/Save All tıklayın ve Menuden Build/Build Solutin tıklayın.
Bu dosyayı kapatın.
Projeyi çalıstırın.
Karsınızda asağıdaki gibi XP stilli bir pencere göreceksiniz.

Sayıları Yazıya Çevirme Örneği
Bu yazıda bir sayının yazıya nasıl çevirebileceğimiz hakkında bir yol göstereceğim, dil
olarak C# kullanılacaktır. Öncelikle belirteyim ki programlama ve C# konusunda çok
yeniyim. Hemen hemen tüm bildiklerimi bu siteye borçluyum.
Asağıdaki kodda bulunan Oku fonksiyonu kendisine string olarak gönderilen tam sayıyı
yazıya çevirmektedir. Kodun çalısma mantığı söyledir.
oku fonksiyonuna gönderilen string basına "0" eklemek suretiyle önce 15 haneye
tamamlanır, sonra yeni string 3 erli kümeler halinde 5 esit parçaya bölünür ve her bir
üçlü küme tek tek
rakam dizisine yüklenir. Böylece 5 elemanlı rakam dizisinin her bir elemanında 3
karakterli bir string yüklü olur.
1.Asama
sayımız 32313234 olsun. ilk olarak sayımızın hane sayısını basına 0 eklemek sureti ile 15
e çıkarırız.
Böylece yeni stringimiz 000000032313234 seklini alır.
2.Asama
Stringimiz 3 erli kümeler halinde 5 esit parçaya bölünür.
1. küme : 000
2. küme : 000
3. küme : 032
4. küme : 313
5. küme : 234
3.asama
her bir küme 5 elemanlı rakam isimli araya yüklenir ve sonuçta
rakam[0] = "000"
rakam[5] = "234"
olur
rakam[5][0]="2" 5. kümenin yüzler basamağı;
rakam[5][1]= "3" 5. kümenin onlar basamağı;
rakam[5][2]= "4" 5. kümenin birler basamağı;
olur.
4.asama
10 elemanlı yüzler, onlar, birler string dizileri tanımlanır ve i çleri doldurulur.
örn:
yuzler.SetValue("ikiyuz",2);
onlar.SetValue("otuz",3);
birler.SetValue("dört",4);
yani yuzler[2]+onlar[3]+birler[4] = ikiyüzotuzdört olur.
int x =Convert.ToInt16(rakam[5][0].ToString()); yüzler
int y =Convert.ToInt16(rakam[5][1].ToString()); onlar
int z =Convert.ToInt16(rakam[5][2].ToString()); birler
yuzler[x]+onlar[y]+birler[z] = ikiyüzotuzdört
bir döngü ile her bir kümeye bu islemi uygularsanız, 1 ve ikinci kümlerin bütün elemanları
sıfır olduğu için sonuçta
otuzdört
üçyüzonuç
ikiyüzotuzdört
ü elde edersiniz
5.Asama
hane isimli 5 li array tanımlanır ve elemanları trilyon, milyar, milyon, bin ve sonuncusu
da bos olacak sekilde ayarlanır. aynı döngü içerisinde her bir kümenin sonuna eklenir
string sonuc = "";
for(int i = 0 ; i < 5;i++)
{
sonuc = sonuc +
yuzler[Convert.ToInt16(rakam[i][0].ToString())]+
onlar[Convert.ToInt16(rakam[i][1].ToString())]+
birler[Convert.ToInt16(rakam[i][2].ToString())]+
hane[i];
}
Burada ayarlanması gereken durum eğer bir kümenin bütün elemanları sıfırsa (yukarıdaki
gibi) hanenin gözükmemesi gerekir. Yani
000 = yuzler[0]+onlar[0]+birler[0]+hane[0] dersek sonuç trilyon olur bu durumda
if(rakam[0].ToString()!= "000")
hane.SetValue("trilyon ",0);
if(rakam[1].ToString()!= "000")
hane.SetValue("milyar ",1);
if(rakam[2].ToString()!= "000")
hane.SetValue("milyon ",2);
if(rakam[3].ToString()!= "000")
hane.SetValue("bin ",3);
yani rakam[0] (trilyon kümesi) "000" değilse hane[0] = "trilyon" olsun demeliyiz.
Yanlız bir sorun daha var. eğer sayı 1000 ise fonksiyon bize haklı olarak "birbin" i
döndürür. Bir milyar var, Bir Milyon var ama bir bin ve bir yüz yok. Ben bu sorunu
BirSorunu isimli fonksiyonla hallettim.
Asağıdaki kodu inceleyebilirsiniz.
using System;
namespace numbereader
{
public class SayiOkuma
{
privatestring[ ] yuzler =newstring[10];
private string[ ] onlar =new string[10];
private string[ ] birler =new string[10];
private string[ ] hane = new string[5];
private string[ ] rakam =newstring[5];
// arrayları tanımlıyoruz
public SayiOkuma()
{
// içlerini dolduruyoruz
yuzler.SetValue("dokuzyüz",9);
yuzler.SetValue("sekizyüz",8);
yuzler.SetValue("yediyüz",7);
yuzler.SetValue("altıyüz",6);
yuzler.SetValue("besyüz",5);
yuzler.SetValue("dörtyüz",4);
yuzler.SetValue("üçyüz",3);
yuzler.SetValue("ikiyüz",2);
yuzler.SetValue("yüz",1);
yuzler.SetValue("",0);
onlar.SetValue("doksan",9);
onlar.SetValue("seksen",8);
onlar.SetValue("yetmis",7);
onlar.SetValue("altmıs",6);
onlar.SetValue("elli",5);
onlar.SetValue("kırk",4);
onlar.SetValue("otuz",3);
onlar.SetValue("yirmi",2);
onlar.SetValue("on",1);
onlar.SetValue("",0);
birler.SetValue("dokuz",9);
birler.SetValue("sekiz",8);
birler.SetValue("yedi",7);
birler.SetValue("altı",6);
birler.SetValue("bes",5);
birler.SetValue("dört",4);
birler.SetValue("üç",3);
birler.SetValue("iki",2);
birler.SetValue("bir",1);
birler.SetValue("",0);
hane.SetValue("",0);
hane.SetValue("",1);
hane.SetValue("",2);
hane.SetValue("",3);
hane.SetValue("",4);
/* ilk olarak bu arrayın elemanlarını bos olarak ayarlıyoruz eğer küme elemanları
000 değilse trilyon,milyar,milyon bin değerleri ile dolduruyoruz
*/
}
public string oku(string sayi)
{
int uzunluk = sayi.Length;
if(uzunluk > 15)
return "Hata girilen değerin uzunluğu en fazla 15 olmalı";
// uzunluk 15 karakterden fazla olmamalı. si
try
{
long k = Convert.ToInt64(sayi);
}
catch(Exception ex)
{
return ex.Message.ToString();
}
sayi = "000000000000000"+sayi;
sayi = sayi.Substring(uzunluk,15);
rakam.SetValue(sayi.Substring(0,3),0);
rakam.SetValue(sayi.Substring(3,3),1);
rakam.SetValue(sayi.Substring(6,3),2);
rakam.SetValue(sayi.Substring(9,3),3);
rakam.SetValue(sayi.Substring(12,3),4);
if(rakam[0].ToString()!= "000")
hane.SetValue("trilyon ",0);
if(rakam[1].ToString()!= "000")
hane.SetValue("milyar ",1);
if(rakam[2].ToString()!= "000")
hane.SetValue("milyon ",2);
if(rakam[3].ToString()!= "000")
hane.SetValue("bin ",3);
string sonuc = "";
for(int i = 0 ; i < 5;i++)
{
sonuc = sonuc + yuzler[Convert.ToInt16(rakam[i][0].ToString())]+
birsorunu(onlar[Convert.ToInt16(rakam[i][1].ToString())]+birler[Convert.ToInt16(raka
m[i][2].ToString())]+hane[i]);
}
return sonuc;
}
privatestring birsorunu (string sorun)
{
string cozum = "";
if (sorun == "birbin ")
cozum = "bin ";
else
cozum = sorun;
return cozum;
}
}
}
Herkese kolay gelsin. Bu arada dileyene DLL i gönderebilirim. Đyi çalısmalar.
C#’ta Temsilci (Delegate)ve Olay(Event) Kullanımı
Bu yazımda sizlere C#’ta delegate(delege veya elçi) ve event(olay) kavramlarından
bahsedeceğim ve elbette bunları kullanmanın yararlarını anlatacağım. Kuskusuz .Net
Framework ile birlikte gelen en büyük yeniliklerden biri delege ve event kullanımıdır. Bu
yanı ile .Net Framework diğer uygulama gelistirme ortamlarının bir kısmından daha üstün
niteliklere sahip olmaktadır. Simdi delege nedir biraz ondan bahsedelim. Delege basit
anlamda düsünürsek aracılık yapandır. Burada ise delege aracılık yapan metod olarak
karsımıza çıkar. Yani baska bir metodun tetiklenmesine yardım eden metottur. Bu sayede
metodların tetiklenmesi dinamik olarak gerçeklesir. Olay(event) ise her zaman –belki
farkında olmadan– kullandığımız kontroller gibidir. Yalnız delege ile olay arasındaki fark
delegelerin "invoke" edilmesi olayların ise "raise" edilmesidir. Daha basit olarak ifade
edersek olaylar delegelerin özel bir halidir. Delegeler olmadan olayların bir anlamı yoktur.
Aslında bir örnekle bunların nasıl kullanıldığını açıklarsam daha iyi anlasılacaktır. Simdi
çayın kaynamasını düsünün. Çay kaynadığı anda haberdar olmak istiyoruz ancak çayın
basında da beklemek istemiyoruz. Bu yüzden bizi haberdar edecek zil çalma metodunu
çayın kaynama metoduna bağlıyoruz. Ve çay kaynadığı anda zil çalma metodu da
otomatik olarak tetiklenmis oluyor. Delege ile olayın implementasyonunu asağıda
göstereceğim ve bunlarla ilgili bir program de koyarak anlasılmasını kolaylastıracağım.
Çünkü bazen programlarla anlatmak istediklerimizi daha iyi anlatırız.
I. Olayın(event) yapılısı: Mouse’ın tıklanması, form üzerindeki bir tusa basılması, form
üzerindeki bir linkin tıklanması vs… bunların hepsi birer olaydır aslında. Simdi mouse
tıklanması ile ilgili bir kodu inceleyelim:
private void FareTikla()
{
// Buraya mouse’un sol tıklanması durumunda yapılması gereken
// islemler gelecek.
}
// Burada fareye sol tıklandığı zaman çalısacak olan metodu bağlıyoruz. Bu islemi de
doğal olarak delegelerle yapıyoruz. Buradaki '+=' operatörüne dikkat ettiniz mi? Bu '+='
operatörü fareye sol tıklanması olayı karsısında raise edilecek eventi bağlamamıza
yarıyor.
Mouse.MouseClicked += new MouseClickedEventHandler(FareTikla);
// Simdi de fare sol tıklandığı zaman çalısacak olan metodu devreden çıkarıyoruz. Bu
sayede fare sol tıklandığı zaman hiç bir metot //devreye girmeyecektir. Yani farenin sol
tıklanmasına herhangi bir tepki verilmeyecektir.
Mouse.MouseClicked -= new MouseClickedEventHandler(FareTikla);
II. Delegenin yapılısı: Delege kullanılacağı zaman tek basına da kullanılabildiği gibi
olaylara bağlanarak da kullanılabilmektedir. Bu yüzden ilk önce olayı(event) anlarsak
delegenin anlasılması daha da kolaylasacaktır. Simdi basit bir delege nasıl tanımlanır ona
bakalım.
// Tanımladığımız delege double tipinde parametre alıyor
// ve int tipinde değer döndürüyor.
public delegate int myDelegate(double D);
// Delegenin temsil edeceği metodu yazalım.
public int ReturnInt(double D)
{
//Buraya metod çalısınca yapılması gerekecek olan kod gelecek
}
// Simdi de delegenin bir örneğini yaratalım.
public void amethod(
{
myDelegate aDelegate = new myDelegate(ReturnInt);
}
//Simdi de delegenin temsil ettiği metodun nasıl çalıstırılacağını sağlayan kodu yazalım.
aDelegate(3333);
Simdi bu delege ve olay yapısı bizim ne isimize yarayacak derseniz; Söyle bir senaryo
üretelim: Birden fazla formda aynı anda çalıstığımızı ve bu formların da birbirinin aynı
olduğunu düsünelim(sekilsel olarak tabii ki) ve bu formlardan herhangi birinin üzerindeki
bir tusa basıldığı zaman bütün formlarda hangi formun tıklandığına dair bir yazı yazmasını
isteyelim.
Bunu eğer uzun yoldan yapmak isterseniz tek tek bütün formlar içinde birer tane
değisken tanımlayıp onu kontrol etmeniz gerekirdi. Sonra da tek tek diğer formlara mesaj
gönderilmesini sağlamak gerekirdi. Bu sekilde yapmak uzun ve zahmetli bir is olurdu.
Đkinci yöntem ise delege(temsilci) yapısını kullanmaktır. Simdi her formumuz için bir tane
forma mesaj yazacak olan bir metod olmalı ve bu metod hangi formdaki yazdırma
tusunun tetiklendiğini belirtmek için bir tane de parametre almalı. Simdi de yukarıda
anlattığımız senaryonun kodunu inceleyelim.
Ana formumuz içinde tanımladığımız delege ve olay yapısına bakalım. Burada delegemiz
tamsayı(int) bir parametre alıyor. Bu parametre sayesinde hangi formdaki tusa basılarak
bu delegenin harekete geçirildiğini anlayabileceğiz. Olayımıza bakacak olursak olayımızın
bir üst satırda tanımlanan delege tipinde olduğunu görebiliriz.
sayac değiskeni ise forma numara vermek için tanımlanmıs bir değisken oluyor.
private int sayac = 0;
public delegate void userDelegate(int formNo);
public event userDelegate userEvent;
Yeni form olusturma tusuna basılınca ise yeni form olusturulacak ve bu formun içindeki
etikete yazma metodu olaya eklenecek.
sayac = sayac+1;
FrmUser frmUser=new FrmUser(sayac,this);
this.userEvent += new userDelegate(frmUser.writeFormNo);
Olaylara doğrudan baska sınıflar altından erisilebilse de bu yöntem pek doğru bir yol
olmayacağı için olayımızı bir metodun içinde kamufle ederek kullanıyoruz. O yüzden
olayımızı metodTetikle adında bir metodun içinde kamufle ederek kullanıyoruz. Baska bir
formdan içinden bu metod çağrıldığı an otomatik olarak olayımızı tetikleyip bütün
formlara(ana formumuz hariç) hangi formdan tusa baıldığını yazacak.
public void metodTetikle(int formNo)
{
userEvent(formNo);
}
Bir de simdi olaydan belli bir delegenin çıkarılması islemi var değil mi? Öyle ya yaratılan
formlardan bir tanesi kapatılırsa yazdırma metodunun tetiklenmesi istisna’ya (exception)
yol açar. Bu yüzden bu formun metodunun listeden çıkarılması gerekir. Asağıdaki kod bu
isi yaparak bizi istenmeyen hatalardan korumus olur.
public void elemanCikart(FrmUser frmUser)
{
this.userEvent -= new userDelegate(frmUser.writeFormNo);
}
Yukarıdaki kodun tamamı ana formumuz içinde yer alan kodlardı. Simdi de ana formda
tusa basılınca olusturulan formun içinde koda bakalım.
Formlara yazı yazdırmak için tusa bastığımızda ana formumuzdaki metodTetikle
metodunu çağırarak olayın tetiklenmesini sağlıyoruz.
private void btnActive_Click(object sender, System.EventArgs e)
{
form1.metodTetikle(formNo);
}
Ancak her formun içinde forma mesajı yazacak olan kısım asağıdaki metoddakigibidir.
Simdi diyeceksiniz, niye aynı form içindeki metodu baska bir formu kullanarak
çağırıyorsunuz. Ama buradaki fark ekrana mesaj yazacak olan form sayısının bir tane
olmamasıdır. N tane forma aynı anda mesaj yazdırmak istediğimiz için bir baska sınıfta
veya formda tanımlanmıs olan bir olayı tetikleyerek bu isi yapıyoruz. Aksi halde N tane
formu tek tek dolasmamız gerekirdi. Bu ise performans kaybına yol açtığı gibi daha büyük
programlarda kod karmasasına yol açabilmektedir.
public void writeFormNo(int tetiklenenNo)
{
lblAciklama.Text="Tusa Basılan Form Numarası:"+tetiklenenNo;
}
Galiba bir tek sonradan olusturulmus bir form kapatılınca ana formumuzdaki delege
çıkartma metodunun nasıl çağrılacağı kaldı. Onu da sonradan olusturulan formumuzun
Dispose metodu içine asağıdaki gibi yazarsak hiç bir sekilde hatayla karsılasmayız.
form1.elemanCikart(this);
Evet yukarıda delege ve olayın nasıl bir arada kullanıldığını basit bir örnek üzerinde
anlatmıs oldum. Yukarıda delege ve olayın bir arada kullanıldığı örnek programı buraya
tıklayarak indirebilirsiniz.
Son olarak iyi anlayamadığınız veya kafanıza takılan kısımlar için mail adresim
aytacozay@msakademik.net
C# ile "Bitmap to Binary" ve "Bitmap Đnceltme" Algoritması
Bu yazıda monochrome(siyah beyaz) Bitmap resimlerinin binary(0,1) formata
dönüstürülmesi ve dönüstürülen binary resim üzerinde inceltme yapma algoritmasının ne
sekilde uygulandığına iliskin örnek uygulama vereceğim.
Yazdığım örnek uygulamanın örnek ekran çıktıları asağıdaki gibidir.
Siyah Beyaz formatındaki A4.bmp resminin ilk görüntüsü binary formatındaki halidir,
ikinci görüntü ise bu binary bilgilerin inceltilmesinden elde edilmistir.
Siyah Beyaz formatındaki B4.bmp resminin ilk görüntüsü binary formatındaki halidir,
ikinci görüntü ise bu binary bilgilerin inceltilmesinden elde edilmistir.
Not : Đki resim arasındaki farkı göremiyorsanız ekranınıza biraz daha uzaktan sası sekilde
bakın.
Yukarıdaki resim dosyalarını projedeki harfler klasöründe bulabilirsiniz.
Projeye ait kaynak dosyaları indirmek için tıklayınız.
Kaynak kodlarla ilgili her türlü sorunuzu bana iletebilirsiniz.
Ref ve Out Anahtar Sözcüklerinin Kullanımı
Bu yazıda C#'ın önemli iki anahtar kelimesi olan ve değer türleride dahil olmak üzere
bütün veri türlerini referans yolu ile metotlara aktarmamızı sağlayan ref ve out'un
kullanımını göstereceğim.
C# dilinde temel olarak iki veri türü vardır. Bunlardan birincisi referans türleri ikincisi
ise değer türleridir. Referans türleri bir ifade(metot çağrımı, atama ifadesi vs.) içinde
kullanıldığı zaman nesnenin bellekteki adresi üzerinden islem yapılır. Yani nesnenin
bütün verisi ayrıca kopyalanmaz. Değer türlerinde ise durum daha farklıdır. Değer
türleri yani int, byte,bool gibi veri türleri herhangi bir ifade içinde kullanılırsa değiskenin
yeni bir kopyası çıkarılır ve islemler bu yeni kopya üzerinden gerçeklestirilir. Dolayısıyla
orjinal nesnenin değeri hiç bir sekilde değistirilemez. Asıl konumuza geçmeden önce
değiskenlerin referans yolu ile aktarımının ne demek olduğunu ve bu iki tür arasındaki
farkı daha iyi anlamak için basit bir örnek vermekte fayda görüyorum.
Asağıdaki programdaki gibi x ve y gibi iki int türünden değiskenimiz olsun. Bu
değiskenlerin değerlerini değistirmek(swap) için Degistir() isimli bir metot yazmak
istediğimizi düsünelim. Bu metot ilk akla gelebilecek sekilde asağıdaki gibi yazılır.
using System;
namespace ConsoleApplication2
{
class Class1
{
static void Main()
{
int x = 10;
int y = 20;
Degistir(x,y);
Console.WriteLine("X = " + x.ToString());
Console.WriteLine("Y = " + y.ToString());
}
static void Degistir(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
}
}
Yukarıdaki programı derleyip çalıstırdığımızda x ve y değiskenlerinin değerlerinin
değistirilmediğini ve aynı değerde kaldıklarını görürüz. Bunun sebebi Degistir()
metoduna gelen x ve y değiskenleri ile Main() metodunda tanımlamıs olduğumuz x ve y
değiskenlerinden tamamen bağımsız yeni değiskenler olmasıdır. Dolayısıyla Degistir()
metodunda yaptığımız değisiklikler orjinal değiskenlerimizi hiç bir sekilde
etkilememistir. Değiskenlerin bu sekilde aktarılmasına "değer yolu ile aktarma" yada
"pass by value" denilmektedir.
Yukarıdaki programı asağıdaki gibi biraz değistirip x ve y değiskenleri bir sınıf
aracılığıyla metota gönderirsek değistirme islemini yapabiliriz.
using System;
namespace ConsoleApplication2
{
class C
{
public int deger;
public C(int x)
{
deger = x;
}
}
class Class1
{
static void Main()
{
C x = new C(10);
C y = new C(20);
Degistir(x,y);
Console.WriteLine("X = " + x.deger.ToString());
Console.WriteLine("Y = " + y.deger.ToString());
}
static void Degistir(C x, C y)
{
int temp = x.deger;
x.deger = y.deger;
y.deger = temp;
}
}
}
Bu progrmı derleyip çalıstırdığımızda x ve y nesnelerinin deger üye elamanlarının yer
değistirildiğini göreceksiniz. Yer değistirmeyi yapabilmemizin sebebi x ve y
değiskenlerinin referans yolu ile metoda geçirilmesidir. Yani Degistir() metodundaki x
ve y değiskenleri ile Main() metodundaki x ve y değiskenleri bellekteki aynı bölgeyi
temsil etmektedirler. Dolayısıyla Degistir() metodunda yaptığımız bir değisiklik Main()
metodundaki değiskene de yansıyacaktır. Nesnelerin metotlara bu sekilde aktarılmasına
ise "referans yolu ile aktarım" yada "pass by reference" denilmektedir.
Bir referans tipi olan string türü ile ilgili önemli bir istisna vardır. string
referans türü olmasına rağmen metotlara string türünden değiskenler
geçirilirken değer tiplerinde olduğu gibi kopyalanarak geçirilirler. Yani int
türünden bir değisken ile string türünden bir değisken metotlara değer yolu ile
aktarılırlar. Bunu test etmek için birinci programdaki int türü yerine string
türünü kullanabilirsiniz.
Ref Anahtar Sözcüğü
Yukarıda denildiği gibi değer tipleri(int, double, byte vs.) metotlara kopyalanarak
geçirilirler yani değiskenin birebir yeni bir kopyası olusturulur. Ancak bazı durumlarda
değer tiplerini de referansları ile metotlara geçirmek isteyebiliriz. C ve C++ dillerinde
değer tiplerini referans yolu ile geçirmek için göstericilerden faydalanır. Yani metotlara
değiskenlerin adresleri geçirilir. C# ta bu islemi yapmak için gösterici yerine yeni bir
anahtar sözcük olan ref kullanılır. ref anahtar sözcüğü değer türlerinin metotlara
referans yolu ile geçirilmesini sağlar. Referans türleri zaten referans yolu ile geçirildiği
için bu türler için ref anahtar sözcüğünü kullanmak gereksizdir. Ancak kullanımı
tamamen geçerli kılınmıstır.
ref sözcüğü metot çağrımında ve metot bildiriminde aynı anda kullanılmalıdır. Yani
metot bildiriminde ref ile birlikte kullanılan bir değisken, metot çağrılırken ref ile
çağrılmalıdır. Yukarıda yazdığımız birinci programı ref sözcüğünün kullanımı ile yeniden
düzenlersek Degistir() metodunun istediğimiz sekilde çalısmasını sağlayabiliriz.
using System;
namespace ConsoleApplication2
{
class Class1
{
static void Main()
{
int x = 10;
int y = 20;
Degistir(ref x,ref y);
Console.WriteLine("X = " + x.ToString());
Console.WriteLine("Y = " + y.ToString());
}
static void Degistir(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
}
}
Bu programı derleyip çalıstırdığımızda x ve y değiskenlerinin değerlerinin değistirildiğini
göreceksiniz. Çünkü Degistir() metoundaki x ve y değiskenleri ile Main() metodundaki x
ve ye değiskenleri aynı bellek bölgesindeki değeri temsil etmektedirler. Birinde yapılan
değisiklik diğerinide etkilemektedir. Yani ilk durumdan farklı olarak ortada iki değisken
yoktur, tek bir değisken vardır.
Unutmamamız gereken nokta metot çağrımının da ref anahtar sözcüğü ile birlikte
yapılması zorunluluğudur. Eğer Degistir() metodunu Degistir(ref x, ref y) yerine
Degistir(x,y) seklinde çağırmıs olsaydık derleme asamasında
Argument '1': cannot convert from 'int' to 'ref int'
Argument '2': cannot convert from 'int' to 'ref int'
hatalarını alırdık.
ref sözcüğünün kullanımı ile ilgili diğer bir önemli nokta ise ref ile kullanılacak
değiskenlere mutlaka değer atanmıs olma zorunluluğudur. Herhangi bir değer
verilmemis değiskeni ref ile de olsa kullanamayız. Kullandığımız takdirde ise derleme
asamasında "Use of unassigned local variable" hatasını alırız. Bu durum ref sözcüğünün
bir kısıtı olarak düsünülebilir. Ancak birazdan göreceğimiz out sözcüğü ile bu kısıtı
ortadan kaldırabileceğimizi göreceğiz.
Out Anahtar Sözcüğü
Out anahtar sözcüğünün kullanım amacı ref anahtar sözcüğünün kullanımı ile tamemen
aynıdır. Yani out ile de değer tipleri referans yolu ile aktarılır. Aralarındaki tek fark out
ile kullanılacak değiskenlere ilk değer verme zorunluluğunun olmamasıdır. Yani ref
sözcüğünün kullanımındaki kısıt, out ile birlikte ortadan kaldırılmıstır. out anahtar
sözcüğünü genellikle bir metottan birden fazla geri dönüs değeri bekliyorsak kullanırız.
Yukarıdaki Degistir() metodunu out ile kullanılabilecek sekilde değistirdiğimizde x ve y
değiskenlerine ilk değer verme zorunluluğumuz kalkacaktır.
ref ve out sözcüklerinin C# dilinde kullanılmasının küçük bir farkı olsada IL dilinde ref
ve out aynı sekilde implemente edilmistir. ILDASM aracı ile Degistir() metodunun hem
ref hemde out versiyonlarının bildiriminin asağıdaki gibi olduğunu görürüz.
.method private hidebysig static void Degistir(int32& x, int32& y) cil managed
{
.maxstack 2
.locals init (int32 V_0)
IL_0000: ldarg.0
IL_0001: ldind.i4
IL_0002: stloc.0
IL_0003: ldarg.0
IL_0004: ldarg.1
IL_0005: ldind.i4
IL_0006: stind.i4
IL_0007: ldarg.1
IL_0008: ldloc.0
IL_0009: stind.i4
IL_000a: ret
}
Bu durum ref ve out sözcüklerinin kullanımındaki farkın C# derleyicisi ile sınırlı
olduğunu göstermektedir. Yani CLR ile ilgili bir fark değildir.
Yazıyı bitirmeden önce out anahtar sözcüğünün kullanımına bir örnek vermek istiyorum.
Đki sayıdan büyük olanına geri dönen bir metot yazmak istediğimizi düsünelim. Aynı
zamanda da bu iki sayıdan birincisinin mi ikincisinin mi büyük olduğunuda bu metotla
öğrenmek istiyoruz. Her metodun tek bir geri dönüs değeri olabileceğine göre klasik
yöntemlerle bunu ideal(!) bir sekilde gerçeklestiremeyiz. Bunun için ilk değer
verilmemis yeni bir parametreyi daha Max() isimli metoda göndereceğiz. Bu parametre
metot içinde değistirilerek kendisini çağıran metoda iletilecektir.
using System;
namespace Out
{
class Class1
{
static void Main()
{
bool b;
int max = Max(9,2,out b);
Console.WriteLine(b);
}
static int Max(int x,int y, out bool b)
{
if(x > y)
b = true;
else
b = false;
return Math.Max(x,y);
}
}
}
Yukarıdaki islemi ref anahtar sözcüğü ile de yapabilirdik ancak bir metodun içinde
değeri belirlenecek bir değiskene ilk değer vermek gereksiz ve mantıksızdır. Dolayısıyla
bir metodun birden fazla değer geri vermesini istediğimiz durumlarda out anahtar
sözcüğünü kullanmamız daha okunabilir ve daha düzenli programcılık açısından
önemlidir.
Bir sonraki yazıda görüsmek üzere...
Abstract Factory Tasarım Deseni(Design Pattern)
Singleton deseni ile basladığım "design pattern" yazı dizisine "Abstract Factory" deseni ile
devam ediyoruz. Bu yazıda "Creational" desenler grubunun en önemli ve en sık kullanılan
deseni olan Abstract Factory(Soyut Fabrika) tasarım deseninin C# ile ne sekilde
uygulandığını bir örnek üzerinden göstereceğim.
Đlk yazımda da bahsettiğim gibi "Creational" grubundaki desenler bir yada daha çok
nesnenin çesitli sekillerde olusturulması ile ilgili desenlerdir. Bu kategoride ele alınan
"Abstract Factory" ise birbirleriyle iliskili yada birbirlerine bağlı olan nesnelerin
olusturulmasını en etkin bir sekilde çözmeyi hedefler. Bu hedefe ulasmak için soyut
sınıflardan(abstract class) veya arayüzlerden(interface) yoğun bir sekilde
faydalanmaktadır. "Abstract Factory" deseninin ana teması belirli sınıfların içerdiği ortak
arayüzü soyut bir sınıf yada arayüz olarak tasarlamaktır. Böylece nesneleri üreten sınıf,
hangi nesnenin üretileceği ile pek fazla ilgilinmesi gerekmez. Đlgilenmesi gereken nokta
olusturacağı nesnenin hangi arayüzleri desteklediği yada uyguladığıdır. Bahsi geçen
mekanizmalarla deseni olusturduğumuz anda çalısma zamanında hangi nesnenin
olusturulması gerektiğini bilmeden nesnelerin olusturulmasını yönetebiliriz.
Eğer bir nesne olusturacaksanız ve tam olarak hangi nesnenin olusturulacağına bir switch
yada if deyimi ile karar veriyorsanız muhtemelen her nesneyi olustruduğunuzda aynı
switch yapısını kullanmak zorunda kalacaksınız. Bu tür tekrarları önlemek için "Abstarct
Factory" deseninden faydalanılabilir. Bu elbetteki nesnelerin ortak bir arayüzü uygulamıs
olma zorunluluğunun getirdiği bir faydadır.
Simdi de gerçek dünyadan bir örnek vererek "Abstract Factory" deseninin hangi
durumlarda kullanabileceğimizi ve soyut fabrika mantığını netlestirelim. Bir CD sürücüsü
düsünün. CD sürücüsü kendisine sürülen CD leri okumakla sorumludur. Hiç bir zaman
sürülen CD nin sekli ve biçimiyle ilgilenmez. Ama sürülen CD nin okunabilmesi için de
belirli sartların yerine getirildiğini farzeder. Yani siz CD sürücüsüne CD olmayan ama CD
ye benzeyen bir cisim yerlestiriseniz onu da okumaya çalısır.(eğer CD sürücünüz
bozulmazsa!) Çünkü okumaya çalıstığı cismin ne olduğu ile pek ilgilenmez CD sürücüsü.
Buradaki örnekte CD sürücüsünün okuma yapabilmesi için gereken sartları bir soyut
fabrika sınıfı ile modelleyebiliriz. Kare yada daire seklindeki gerçek CD ler ise bu soyut
fabrika sınıfı tarafından belirlenen sartları destekleyen gerçek nesnelerdir. CD
sürücüsünün kendisi ise soyut fabrika tarafından belirlenen standartlar çerçevesi
içerisinde CD nin ne tür bir CD olduğundan bağımsız bir sekilde bilgiyi okuyan birimdir.
Bu, "abstract factory" desenindeki client yani istemci sınıfa denk düser ki bu sınıf
nesnelerin yaratılmasından sorumludur.
Bu giris bilgilerinden sonra "abstract factory" deseninin temel özelliklerini kısaca
özetleyelim.
• "Abstract Factory", nesneleri olusturan bir sınıftır. Olusturulan bu nesneler
birbirleriyle iliskili olan nesnelerdir. Diğer bir deyisle aynı arayüzü uygulamıs olan
nesnelerdir.
• Üretilen nesnelerin kendisiyle ilgilenilmez. Đlgilenilen nokta olusturulacak
nesnelerin sağladığı arayüzlerdir. Dolayısıyla aynı arayüzü uygulayan yeni
nesneleri desene eklemek çok kolay ve esnektir.
• Bu desende üretilecek nesnelerin birbirleriyle iliskili olması beklenir.
UML Modeli
Asağıdaki sekil "abstract factory" tasarım deseninin yapısal UML diagramını
göstermektedir. Semadaki her bir sekil desendeki bir sınıfı modellemektedir. Ayrıca
desendeki sınıflar arasındaki iliskilerde detaylı bir sekilde gösterilmistir.
Yukarıda semayı kısaca açıklamakta fayda var. Semadan da görüleceği üzere "abstract
factory" deseninde 3 ana yapı vardır. Đlk yapı nesnelerin olusturulmasından sorumlu
soyut ve gerçek fabrikalar, ikinci yapı soyut fabrikadan türeyen gerçek fabrikaların
ürettiği ürünleri temsil eden soyut ve gerçek ürün sınıflar, son yapı ise herhangi bir
ürünü, kendisine parametre olarak verilen soyut fabrikaları kullanarak üreten
istemci(client) sınıfıdır.
SoyutFabrika sınıfı gerçek fabrikaların uygulaması gereken arayüzü temsil eder. Bu sınıf,
bütün metotları soyut olan sınıf olabileceği gibi bir arayüz de olabilir. Uygulamanızın
ihtiyacına göre dilediğinizi kullanabilirsiniz. SoyutFabrika sınıfında ürün1 ve ürün2'nin
üretilmesinden sorumlu iki tane metot bulunmaktadır. Dolayısıyla bütün gerçek
fabrikaların hem ürün1'i hemde ürün'yi ürettiği kabul edilmektedir. Her bir ürünün ortak
özelliklerini belirlemek ve ana yapıda toplamak için SoyutUrun1 ve SoyutUrun2 sınıfları
olusturulur. Bu sınıflarda herhangi bir ürüne özel bilgi bulunmamaktadır. Asıl bilgi bu
soyut ürünlerden türeyen GercekUrun sınıflarında bulunmaktadır. Her bir fabrikanın
ürettiği ürünleri modelleyen sınıflarda yukarıdaki sekilde gösterilmistir. Asıl önemli mesele
ise gerçek fabrikaların üretimden sorumlu metotlarının ne sekilde geri döneceğidir.
Yukarıdaki semadan da görüleceği üzere bu metotlar üreteceği ürünün soyut sınıfına
dönmektedir. Yani üretim sonucunda geri dönen gerçek ürün nesnesi değildir. Semada
Client olarak gösterilen sınıfın yapısı ise su sekildedir : Client sınıfı yapıcı metoduna bir
soyut fabrika nesnesi alır. Ve soyut fabrikanın üretimden sorumlu metotlarını kullanarak
soyut ürünleri üretir. Dikkat ederseniz Client sınıfı hangi gerçek fabrikanın üretim
yaptığından ve üretilen ürünün gerçek özelliklerinden haberi yoktur. Client sadece soyut
fabrikanın içerdiği temel özelliklerin farkındadır. Bunu semadaki kalın ve kesikli oklardan
görmek mümkündür.
Desenin C# ile Gerçeklestirilmesi
Yukarıdaki yapısal örneği verdikten sonra gerçek bir örnek ile bu deseni nasıl
gerçeklestirebileceğimizi inceleyelim. Bu örnekte araba kasası ve araba lastiği üreten
farklı iki firmanın üretimi modellenmektedir.
Önce örneği kabaca inceleyin, ardından açıklamaları okuyun.
using System;
namespace DesignPattern
{
abstract class SoyutArabaFabrikasi
{
abstract public SoyutArabaKasasi KasaUret();
abstract public SoyutArabaLastigi LastikUret();
}
class MercedesFabrikasi : SoyutArabaFabrikasi
{
public override SoyutArabaKasasi KasaUret()
{
return new MercedesE200();
}
public override SoyutArabaLastigi LastikUret()
{
return new MercedesLastik();
}
}
class FordFabrikasi : SoyutArabaFabrikasi
{
public override SoyutArabaKasasi KasaUret()
{
return new FordFocus();
}
public override SoyutArabaLastigi LastikUret()
{
return new FordLastik();
}
}
abstract class SoyutArabaKasasi
{
abstract public void LastikTak(SoyutArabaLastigi a );
}
abstract class SoyutArabaLastigi
{
}
class MercedesE200 : SoyutArabaKasasi
{
public override void LastikTak(SoyutArabaLastigi lastik)
{
Console.WriteLine( lastik + " lastikli MercedesE200");
}
}
class FordFocus : SoyutArabaKasasi
{
public override void LastikTak(SoyutArabaLastigi lastik)
{
Console.WriteLine( lastik + " lastikli FordFocus");
}
}
class MercedesLastik : SoyutArabaLastigi
{
}
class FordLastik : SoyutArabaLastigi
{
}
class FabrikaOtomasyon
{
private SoyutArabaKasasi ArabaKasasi;
private SoyutArabaLastigi ArabaLastigi;
public FabrikaOtomasyon( SoyutArabaFabrikasi fabrika )
{
ArabaKasasi = fabrika.KasaUret();
ArabaLastigi = fabrika.LastikUret();
}
public void LastikTak()
{
ArabaKasasi.LastikTak( ArabaLastigi );
}
}
class UretimBandi
{
public static void Main()
{
SoyutArabaFabrikasi fabrika1 = new MercedesFabrikasi();
FabrikaOtomasyon fo1 = new FabrikaOtomasyon( fabrika1 );
fo1.LastikTak();
SoyutArabaFabrikasi fabrika2 = new FordFabrikasi();
FabrikaOtomasyon fo2 = new FabrikaOtomasyon( fabrika2 );
fo2.LastikTak();
}
}
}
Yukarıdaki örnekte SoyutArabaFabrikasi sınfı iki metot içermektedir. Bu metotlar
SoyutArabaFabrikasi sınıfından türeyecek sınıfların uygulaması gereken metotlardır.
Çünkü metotlar abstract olarak bildirilmistir. Bu metotlar gerçek fabrika sınıflarının araba
kasası ve araba lastiği üretmesi gerektiğinin belirtisidir. Zira görüldüğü üzere
SoyutArabaFabrikasi sınıfından türeyen MercedesFabrikasi ve FordFabrikasi kendilerine
has lastikleri ve kasaları üretmek için soyut fabrika sınıfının metotlarını kullanmaktadır.
Bu metotlar geri dönüs değeri olarak soyut ürün sınıflarını temsil eden sınıfları
döndürmektedirler. Örneğin KasaUret() metodu her bir fabrika için farklı ürün üretmesine
rağmen her bir ürün SoyutArabaKasasi sınıfındaki metotları uyguladığı için gerçek ürünler
birbirleyile iliskili hale gelir. Mercedes fabrikası KasaUret() metodu ile MercedesE200
ürününü döndürmesine rağmen Ford fabrikası aynı metotla FordFocus ürününü
döndürmektedir. Ancak her iki fabrikanın da ürettiği ürün SoyutArabaKasasi sınıfından
türediği için herhangi bir çeliski olmamaktadır.
SoyutArabaKasasi sınıfındaki LastikTak() sınıfı fabrikadan üretilen ürünlerin birbirleriyle
karıstırılmadan esnek bir sekilde nasıl iliskilendirildiğini gösterilmektedir. Bu metot
parametre olarak gerçek lastik ürünü yerine soyut lastik ürünü alır. Dolayısıyla herhangi
bir fabrikadan üretilen lastik ürünü bu metoda parametre olarak geçirilebilir.
FabrikaOtomasyon sınıfı kendisine verilen bir soyut fabrika nesnesi üzerinden kasa ve
lastik üretir ve üretilen lastiği, lastiğin gerçek türünü bilmeden üretilen araba kasası ile
iliskilendirir. Dikkat ederseniz bu sınıf üretimin yapılacağı fabrikanın hangi fabrika olduğu
ve üretilen ürünlerin gerçekte hangi ürünler olduğu ile ilgilenmez.
Son olarak tasarladığımız bütün bu sınıfları test edecek UretimBandı sınıfını inceleyelim.
Bu sınıf içerisinde ürünleri üretilecek fabrikanın soyut nesnesi olusturulur ve
FabrikaOtomasyonu nesnesine parametre olarak verilir. SoyutFabrika nesnesini alan
FabrikaOtomasyonu bu nesnenin standart üretimden sorumlu metotlarını kullanarak kasa
ve lastik üretir. Ardından SoyutArabaKasasi sınıfının LastikTak() metodunu kullanarak
kasa ve lastik ürünlerini iliskilendirir.
Bu örnekte kullanılan soyut sınıfların yerine arayüzleride kullanmak mümkündür. Daha
önce de dediğim gibi siz uygulamanızın durumuna göre herhangi birini seçebilirsiniz.
Diğer bir "Creational" deseni olan "Builder" desenini anlatacağım yazıda görüsmek
üzere...
C# ile .NET Ortamında Threading'e Giris
Đnsan vücudunda aynı anda bir çok is birlikte yapılır, mesela kalbimiz tüm vücuda kan
pompalarken midemiz yediğimiz bir seyi sindirmek için gerekli enzimleri salgılar:
Bilgisayarların zaman içinde çok hızlı gelismeleri sonucunda insanlar bu aletlerden daha
fazla verim ve hız beklediler ve ortaya atılan birçok çözümden biri de is parçacıklarını
(threads) kullanmak olmustur.
Đs parçacıkları ilk defa Ada programlama dilinde Amerikan ordusunun stratejik yazılımları
için kullanılmıstır. Daha sonra C++ dilinde is parçacıklarını kullanmak için kütüphaneler
gelistirilmistir. Bu kütüphaneler sayesinde zaman içinde C++ dilinde yazılmıs
programlarda is parçacıklarını kullanmak bir takım faydalar sağlamıstır.
Đs parçacıklarını .NET ortamında nasıl kullanacağımızı öğrenmeden önce isin teorik
temellerini bilmek gerekir. Ayrıca is parçacıklarını ne zaman ve nasıl programlarımıza
katmayı da öğrenmek daha sağlıklı programlar gelistirmeye yardımcı olacaktır.
Makalemizin kalan kısmını MSDN kütüphanesindeki is parçacıkları konusunun bas
kısımları olusturacaktır.
Đsletim sistemlerinde aynı anda birden fazla programın çalısması günümüzde mümkün
hale gelmistir. Aslında bir islemcide aynı anda sadece bir islem gerçeklesebilir. Fakat
baslayan bir islemin tamamını bitirmeden baska bir islemin yapılması ile multitasking
basarılabalir. Mesela bir taraftan Ms Word diğer taraftan Ms Explorer açık olabilir.
Đslemlerin bir alt parçası olan is parçaçıkları (threadler) aynı zamanda bilgisayar
ortamında yapılacak olan en küçük görev birimleridir denilebilir. Bir islem(process) içinde
birden fazla is parçacığı bulunabilir. Her bir is parçacığı için hata yönetimi(expection
handler), öncelik çizelgesi (scheduling priority) ve bir takım yapılar bulunur. Bir önceki
cümlede bahsettiğimiz yapılar is parçacığı hakkında isletim sisteminin tuttuğu bilgilerdir.
Bu bilgiler ile is parçacıklarının sorunsuz olarak çalıstırılması sağlanır.
.NET platformu islemleri(process) daha küçük bir birim olan application domain'lere
ayırır. Application domain'ler System.AppDomain sistem alanındaki sınıflar tarafından
islenirler. .NET'te bir veya daha fazla is parçacığı birden farklı application domain için
çalısabilir. Her ne kadar bir application domain sadece bir tane is parçacığı ile çalısmaya
baslasa da zaman içinde birden fazla application domain ve is parçacığı aynı application
domain için çalısabilir. Ayrıca tek bir is parçacığı birden farklı application domain'ler
arasında gidip gelebilir.
Eğer bir isletim sistemi preemtive multitasking'i destekliyorsa bilgisayarda birden fazla
programın aynı anda çalısıyormus hissi yaratılabilir. Bunun için isletim sistemi her bir
islemin belirli bir süre (time slicing) islemciyi mesgul etme hakkı tanır. Đslemci kullanım
süresi dolan islem bekletilmeye alınır ve bu islem hakkındaki bilgiler bir yere not edilir.
Sonra sırada bekleyen (thread queue) baska bir islemin belirli bir süre islemciyi
kullanmasına izin verilir. Đkinci islemin de süresi dolunca bu islem hakkında bilgiler bir
yere kaydedilir ve tekrar kuyruğa geri döner. Sonra sıradaki diğer isleme baslanır... Ve
bu sekilde devam eder.
Đslemlerin islemciyi kullanma aralıkları isletim sistemine ve islemciye göre değisir.
Đslemciyi kullanma aralıkları o kadar küçük ve islemciler o kadar hızlıdır ki bir çok is
parçacığının çalıstırıldığı bir isletim sisteminde aynı anda birden farzla programın çalıstığı
hissi kullanıcıda uyanır.
Ne Zaman Birden Fazla Đs Parçacığı ile Çalısmalı?
Eğer gelistirdiğimiz programlar kullanıcı ile sık sık etkilesime geçiyor ve kullanıcılara
sistemin cevabının çok hızlı olması gerekiyorsa is parçacıklarını kullanmak yerinde
olacaktır. Eğer sizin programınızda sadece bir is parçacığı yeterli oluyorsa ve .NET
remoting veye XML Web servisleri kullanıyorsanız yine is parçacıklarından faydalanmak
suretiyle programınızın kullanıcıya vereceği tepkiyi daha kısa sürede üretebilirsiniz. Son
olarak yoğun bir biçimde I/O islemleri gerektiren programlarda is parçacıklarından
faydalanmak uygun olacaktır.
Çoklu Đs Parçacıklarının Avantajları
Birden fazla is parçacığı kullanmakla hem programın kullanıcıya olan cevap süresi
(Kullanıcı arayüzünde) kısalır hem de aynı anda arka planda verilerin hızlıca islenip
sonuca ulasılması sağlanır. Mesela biz bir taraftan Excel çalısma sayfasına verileri
giriyorken diğer taraftan Excel çalısma sayfasında tanımlanan formüllere göre diğer
hücrelerin değerlerini hesaplayıp yazar.
Bir programda hem is parçacıkları kullanılır hem de bu program birden fazla islemcisi olan
bir makinada çalıstırılırsa kullanıcıların programdan memnuniyetlerinde çok ileri seviyede
artıslar olur. Gelistirdiğimiz bir programda birden fazla is parçası kullanmakla:
• Ağ üzerinde, web sunucu ile yada veritabanı ile veri alısverisi
• Çok uzun süren hesaplamalı islemleri
• Farklı öncelikteki görevlerde. (Mesela yüksek öncelikli is parçacıkları ile hemen
bitirilmesi gereken islemler yapılırken diğer is paraçıkları baska isleri yapabilir.)
• Grafik arayüzünde kullanıcıya daha hızlı cevap verilirken arka planda diğer veri
isleme isleri gerçeklesir.
Çoklu Đs Parçacıklarının Dezavantajları
Mümkün olduğunca az sayıda is parçacığını aynı anda kullanmak tavsiye edilir. Bu sekilde
isletim sisteminin daha az kaynağını kullanır ve performansı artırabiliriz. Ayrıca is
parçacıkları için hem ek kaynak gereksimi hem de programda çakısma ihtimalleri vardır.
Đs parçacıkları için gerekli ek kaynaklar sunlardır:
• Đsletim sistemleri islemler, AppDomain nesneleri ve is parçacıkları hakkında
bilgileri tutmak zordundadırlar. Yani, islemler, AppDomain nesneleri ve is
parçıklarının olusturulmasında kullanılabilir hafıza sınırlayıcı bir etken olabilir.
• Çok sayıda is parçacıkları ile çalısma durumlarında, islemci is parçacıklarının
gerektirdiği isleri yapmaktan çok is parçacıkları arasında geçis için mesgul olur.
Ayrıca bir islemin içinde çok sayıda is parçacığı varsa bu is parçacıklarının
islenmesi için daha az sayıda sans doğacaktır.
• Birden fazla is parçacığı aynı anda islemye çalısmak çok karmasık bir durum
yaratır ve bir çok hataya sebep olabilir.
• Bir is parçağınının isi bittiğinde onu yok etmek için yine çok karmasık kodlar
yazmak ve sonuçlarını tahmin etmek gerekir.
Kaynakları paylasmak ve birden fazla islem için aynı anda kullanmaya çalısmak sistemde
çakısmalara yol açabilir. Muhtemel çakısmaların önüne geçmek için senkronizasyon
yapmak veya paylasılan kaynaklara erisimi kontrol altına almak gerekir. Aynı veya farklı
AppDomain'lerde erisimleri senkronize etmede basarısızlık durumumda deadlock (aynı
anda iki is parçacığının bos durdukları halde birbirlerini sonsuza kadar beklemleri)
problemi ortaya çıkabilir. Fakat sistemin sağladığı senkronize nesneleri ile aynı kaynağın
farklı farklı is parçacıkları tarafından kullanılması koordine edilebilir. Tabiki is
parçacıklarının sayısını azaltmak da kaynakların senkronize olarak kullanılmasını
kolaylastırır.
Senkronize edilmesi gereken kaynaklar sunlardır:
• Sistem kaynakları (iletisim portları gibi)
• Birden fazla islem tarafından kullanılan kaynaklar.(dosya yöneticileri)
• Tek bir AppDomain'e ait (global, satic ve örnek veri alanları) fakat farklı is
parçacıkları tarafından kullanılan kaynaklar.
Đs Parçacıkları ve Uygulama Tasarımı
Genelde kısa sürecek görevler ve özel olarak zamanlama gerektirmeyen is parçacıkları
için ThreadPool sınıfını kullanmak en kolay yoldur. Fakat bir çok sebebten dolayı
kendimize ait thread sınıfını yazmak daha iyi olacaktır. Bunun en önemli nedenleri
sunlardır:
• Eğer özel önceliğe sahip bir görev tanımlayacak ve kullanacaksak.
• Eğer uzun sürebilecek bir is yapmak gerekiyorsa.
• Eğer is parçacıklarınızı tek-is parçacığı aparmanına koymak gerekiyorsa
(ThreadPool sınıfındaki tüm is parçacıkları çoklu-isparçacığı apartmanına koyulur.)
• Eğer bir is parçacığı için sabit bir kimlik kullanmak gerekiyorsa .
Đleriki yazılarda .NET ortamında C# ile is parçacıklarının kullanımlarını daha detaylı olarak
inceleyeceğiz ve bir çok örnek kod üzerinde duracağız.
C# ile Đlgili Sık Sorulan Sorular (SSS)
Bu yazıda C# dili ilgili sık sorulan sorulara yanıt verilmistir.
Asağıdaki C# ile ilgili sık sorulan sorular www.msdn.com adresinde faaliyet
gösteren Microsoft Visual C# ekibi tarafından hazırlanmıstır.
S - 1 : DllImport niteliğini neden çalıstıramıyorum?
C - 1 : DllImport ile isaretlenen bütün metotlar public static extern olarak
bildirilmelidir.
S - 2 : Yazdığım switch ifadeleri farklı bir biçimde çalısıyor. Neden?
C - 2 : C# case blokları için "explicit fall through" özelliğini desteklemez. Buna göre
asağıdaki kod parçası geçersizdir ve C#'ta derlenemez.
switch(x)
{
case 0:
// bir seyler yap
case 1:
// 0 case'indekine ek olarak birseyler daha yap
default:
// 0 ve 1 durumlarına ek olarak birseyler daha yap
break;
}
Yukarıdaki kodun verdiği etkiyi C# ile asağıdaki gibi gerçeklestirrebiliriz. (Case' ler
arasındaki akısın açıkça belirtildiğine dikkat edin!)
class Test
{
public static void Main()
{
int x = 3;
switch(x)
{
case 0:
// bir seyler yap
goto case 1;
case 1:
// 0 case'indekine ek olarak birseyler daha yap
goto default;
default:
// 0 ve 1 durumlarına ek olarak birseyler daha yap
break;
}
}
}
S - 3 : const ve static readonly arasındaki farklar nelerdir?
C - 3 : static readonly elemanlar bulundukları sınıfın üye elemanları tarafından
değistirilebilir(!), fakat const olan üye elamanlar asla değistirilemez ve derleme zamanı
sabiti olarak ilk değerleri verilmelidir.
static readonly üye elemanlarının değistirilebilmesini biraz açacak olursak, static readonly
üyeyi içeren sınıf bu üyeyi asağıdaki durumlarda değistirebilir :
- değisken ilk değer verilen durumda
- static yapıcı metotlar içinde
S - 4 : trace ve asssert komutlarını nasıl gerçekleyebilirim?
C - 4 : Metotlarla birlikte Conditional niteliğini kullanarak gerçekleyebiliriz.
class Debug
{
[conditional("TRACE")]
public void Trace(string s)
{
Console.WriteLine(s);
}
}
class MyClass
{
public static void Main()
{
Debug.Trace("hello");
}
}
Yukarıdaki örnekte Debug.Trace() metodu ancak ve ancak TRACE önislemci seöbolü
tanımlanmıssa çağrılacaktır. Komut satırından ön islemci sembollerini tanımlamak için /D
parametresi kullanılabilir. Conditional niteliği ile bildirilen metotların geri dönüs değerinin
void olma zorunluluğu vardır.
S - 5 : C#'ta dll olusturmak için ne yapmalıyım?
C - 5 : Derleyicinin /target:library argümanını kullanmanız gerekir.
S - 6 : checked isimli bir değisken tanımladığımda neden derleme zamanında "syntax
error" hatası alıyorum?
C - 6 : Çünkü checked C#'ta bir anahtar sözcüktür.
S - 7 : Bir yapıcı metot içinde asırı yüklenmis baska bir yapıcı metot nasıl çağrılır (this()
ve yapıcımetotadı() seklindeki çağrımlar derlenmiyor)?
C - 7 : Diğer bir yapıcı metot asağıdaki gibi çağrılabilir.
class B
{
B(int i)
{ }
}
class C : B
{
C() : base(5) //
B(5) i çağırır.
{ }
C(int i) : this() //
C() yi çağırır.
{ }
public static void
Main() {}
}
S - 8 : C#'ta Visual J++ ta bulunan instanceof operatörünün karsılığı varmıdır?
C - 8 : Evet, is operatörü bunun karsılığıdır. Kullanımı asağıdaki gibidir :
ifade is tür
S - 9 : C#'ta enum sabitleri nasıl kullanılır.
C - 9 : enum türlerinin kullanımına bir örnek :
namespace Foo
{
enum Colors
{
BLUE,
GREEN
}
class Bar
{
Colors color;
Bar() { color = Colors.GREEN;}
public static void Main() {}
}
}
S - 10 : Geri dönüs değeri olmayan bir metot bildirimi yaptığımda neden (CS1006) hatası
almaktayım?
C - 10 : Bir metodun geri dönüs değerini yazmadan bildirirseniz derleyici onu sanki bir
yapıcı metot bildiriyormussunuz gibi davranır. O halde geri dönüs değeri olmayan bir
metot bildirimi için void anahtar sözcüğünü kullanın. Asağıda bu iki kullanıma örnek
verilmistir.
// Bu bildirim CS1006 hatası verir.
public static staticMethod (mainStatic obj)
// Bu metot ise istenildiği gibi çalısır.
public static void staticMethod (mainStatic obj)
S - 11 : Her birinde farklı Main() metodu olan birden fazla kaynak kod dosyam var:
derleme sırasında hangi Main() metodunun kullanılacağını nasıl bildirebilirim?
C - 11 : Programınızın giris noktası(metodu) Main isimli herhangi bir parametre almayan
yada string türünden bir dizi parametresi alan geri dönüs değeri void yada int olan static
bir metot olmalıdır.
C# derleyicisi programınızda birden fazla Main metodu bildirmenize izin verir fakat hangi
Main() metodunu kullanacağınızı derleme zamanında bildirmeniz gerekir. Main()
metodunu belirtirken Main metodunun bulunduğu sınıfın tam yolunu belirtmeniz gerekir.
Komut satırından kullanılan /main argümanı bu ise yarar.(Örn : csc /main:MainSınıfı *.cs)
S - 12 : Console.WriteLine() metodu bir string içinde NULL karakteri gördüğünde ekrana
yazma islemini durdururmu?
C - 12 : Çalısma zamanı için string türleri NULL ile sonlandırılmıs türler değildir.
Dolayısıyla bir string içine NULL karakteri gömebilirsiniz. Console.WriteLine() ve buna
benzer metotlar string değiskeninin sonuna kadar islem yaparlar.
S - 13 : C# ta "Multicast Delegate"(çoklu temsilciler) bildirmek mümkünmüdür,
mümkünse sentaksı nasıldır?
C - 13 : Bütün temsilciler varsayılan olarak multicast olarak bildirilir. Dolayısıyla Visual
J++ taki gibi ayrıca multicast anahtar sözcüğü yoktur.
S - 14 : Delegate/MulticastDelegate (Temsilciler) nasıl bildirilir?
C - 14 : C# ta temsilci bildirimi için sadece bir parametreye ihtiyacımız vardır : metot
adresi. Diğer dillerden farklı olarak C# ta metodun adresi aynı zamanda bu metodun
hangi nesne üzerinden de çağrılacağını tutabilir, diğer dillerde ise temsilcilern temsil
etttiği metodu çağırabilmek için ayrıca nesnelere ihtiyaç duyulur. Örneğin
System.Threading.ThreadStart() metodunun kullanımına bakalım.
Foo MyFoo = new Foo();
ThreadStart del = new ThreadStart(MyFoo.Baz);
Bu, static ve instance metotlarının aynı sentaks ile çağrılabileceğini göstermektedir.
S - 15 : Yaptığım windows pencere uygulamasını her çalıstırdığımda neden pop up
seklinde konsol ekranı gösteriliyor.
C - 15 : Proje ayarlarında "Target Type" özelliğinin Console Application yerine Windows
Application olduğuna emin olun. Eğer komut satırı derleyicisini kullanıyorsanız /target:exe
argümanı yerine /target:winexe argümanını kullanın.
S - 16 : Gereksiz çöp toplayısınıcı(Garbage Collection) zorla çağırmanın bir yolu var mı?
C - 16 : Evet; Bütün referasnları null değer atayın ve System.GC.Collect() statik
metodunu çağırın.
Yıkılması(destruct) gereken nesneleriniz var ve GC nin bunu yapmadığını düsünüyorsanız
nesneleri null değere atayarak onların sonlandırıcı metotlarının çağrılmasını sağlayın ver
ardından System.GC.RunFinalizers() metodunu çağırın
S - 17 : C#, C dilindeki makroları destekliyormu?
C - 17 : Hayır, C# ta makro yoktur.
__LINE__ ve __FILE__ gibi C dilinde önceden tanımlanmıs bazı makroların
System.Diagnostics isim alanındaki StackTrace ve StackFrame gibi COM+ ile ilgili
sınıflardan elde edilebileceğini unutmayın. Fakat bunlar sadece Debug moddaki derleme
için çalısacaktır.
S - 18 : C# derleyicisine bazı dll leri referans vermememe rağmen neden kendisi
referans verir.
C - 18 : "csc.rsp" dosyasında bulunan bütün assembly lere C# derleyicisi otomatik olarak
referans verir. Bu dosyanın içerdiği assembly leri /r argümanı ile belirtmek zorunda
değilsiniz. csc.rsp dosyasının kullanımını komut satırından /noconfig argümanını belirterek
engelleyebilirsiniz.
Not : Visual Studio IDE si hiç bir zaman csc.rsp dosyasını kullanmaz.
S - 19 : Delegate/MulticastDelegate (Temsilciler) nasıl bildirilir?
C - 19 : Asağıda DllImport niteliğinin kullanımına bir örnek verilmistir.
using System.Runtime.InteropServices;
class C
{
[DllImport("user32.dll")]
public static extern int MessageBoxA(int h, string m, string c, int type);
public static int Main()
{
return MessageBoxA(0, "Hello World!", "Caption", 0);
}
}
Yukarıdaki örnek kod yönetilmeyen(unmanaged) DLL deki doğal(native) bir fonksiyonu
C# ta bildirmek için minumum gereksinimleri gösterir.C.MessageBoxA() metodu static ve
extern sözcükleri ile bildirilmis, DllImport niteliği ile bu metodun user32.dll dosyasında
MessageBoxA ismiyle uygulanmıs olduğu belirtilmektedir.
S - 20 : COM+ runtime'ında tanımlanan bir arayüzü uygulamaya çalısıyorum ancak
"public * Object GetObject{...}" çalısmıyor gibi. Ne yapmalıyım?
C - 20 : Managed C++'ta "Object * GetObject()"(object türünden gösterici) sentaksı
geçerlidir. C# ta ise "public Object GetObject()" biçiminde kullanmak yeterlidir.
S - 21 : C# sablon(template) yapılarını destekliyormu?
C - 21 : Hayır, fakat bir tür sablon olan generics yapılarının C# diline eklenilmesi
planlanmaktadır. Bu türler sentaks olarak sablonlara benzerler fakat derleme zamanı
yerine çalısma zamanında olusturulurlar. Bu türlerle ilgili detaylı bilgi için tıklayın.
S - 22 : Item özelliğini kullandığımda neden CS0117 hatası almaktayım?
C - 22 : C# özellikleri destekler ancak Item özelliğinin sınıflar için özel anlamı vardır.
Item özelliği aslında varsayılan indeskleyici olarak yer alır. Bu imkanı C# ta elde etmek
için Item sözcüğünü atmak yeterlidir. Asağıda örnek program gösterilmistir.
using System;
using System.Collections;
class Test
{
public static void Main()
{
ArrayList al = new ArrayList();
al.Add( new Test() );
al.Add( new Test() );
Console.WriteLine("First Element is {0}", al[0]);
}
}
WriteLine metodunda .Items[0] 'ın kullanılmadığına dikkat edin.
S - 23 : Herhangi bir fonksiyonumu "out int" parametresi alacak sekilde tasarlmaya
çalısıyorum. Bu metoda göndereceğim int değiskenini nasıl bildirmeliyim?
C - 23 : Değisken bildirimi int türünden yapmalısınız fakat bu değiskeni fonksiyona
parametre olarak gönderirken asağıdaki gibi "out" anahtar sözcüğünü de kullanmalısınız.
int i;
foo(out i);
foo metodu asağıdaki gibi bildirilmistir.
[return-type] foo(out int o) { }
S - 24 : C++'taki referanslara benzer bir yapı C#' ta varmıdır? (Ör : void foo(int &x) gibi
)
C - 24 : C#'ta bunun karsılığı ref parametreleridir.
class Test
{
public void foo(ref
int i)
{
i = 1;
}
public void bar()
{
int a = 0;
foo(ref a);
if (a == 1)
Console.WriteLine("It
worked");
}
public static void
Main() {}
}
Not: Metot çağrımında da ref sözcüğünün kullanıldığına dikkat edin!
S - 25 : C#'ta inout argümanları nasıl bildirilir?
C - 25 : inout'un C# taki karslığı ref'tir. Örneğin :
public void MyMethod (ref String str1, out String str2)
{
...
}
Bu metot asağıdaki biçimde çağrılmalıdır.
String s1;
String s2;
s1 = "Hello";
MyMethod(ref s1, out s2);
Console.WriteLine(s1);
Console.WriteLine(s2);
Not : Hem metot çağrımı hemde metot bildirimi sırasında ref sözcüğünün kullanıldığına
dikkat edin.
S - 26 : Yıkıcı metotlar(destructors) ve GC C#'ta ne sekilde çalısır?
C - 26 : C# ta sonlandırıcı metotlar vardır ve kullanımı asağıdaki gibidir. (Bu sonlandırıcı
metotlar C++ taki yıkıcı metotlara benzer, tek farkı çağrılacağı garanti altına
alınmamıstır.)
class C
{
~C()
{
// your code
}
public static void Main() {}
}
Bu metotlar object.Finalize() metodunu asırı yüklerler ve GC nesneyi yok ederken bu
metodu kullanır.
S - 27 : Derleme sırasında neden "CS5001: does not have an entry point defined -
tanımlanmıs giris noktası yok- " hatasını alıyorum?
C - 27 : Bu hata en çok Main metodunu main seklinde yazdığınızda karsınıza çıkar. Giris
noktası olan bu Main metodunun bildirimi asağıdaki gibi olmalıdır :
class test
{
static void Main(string[] args) {}
}
S - 28 : Visual J++ ta "synchronized" olarak bildrilen metotları C# diline nasıl tasırım?
C - 28 : Orjinal Visual J++ kodu:
public synchronized void Run()
{
// function body
}
C# diline tasınmıs hali
class C
{
public void Run()
{
lock(this)
{
// function body
}
}
public static void Main() {}
}
S - 29 : Kanal(thread) senkronizasyonu(Object.Wait, Notift ve CriticalSection) C#'ta nasıl
sağlanır?
C - 29 : lock ile isaretlemis bloklar bu ise yarar :
lock(obj)
{
// code
}
kod parçasının karsılığı
try
{
CriticalSection.Enter(obj);
// code
}
finally
{
CriticalSection.Exit(obj);
}
S - 30 : Statik yapıcı metotların sentaksı nasıldır?
C - 30 : Asağıda MyClass adlı sınıfın statik yapılandırıcısının bildirimi gösterilmistir.
class MyClass
{
static MyClass()
{
// initialize static variables here
}
public static void Main() {}
}
S - 31 : Bir özelliğin get ve set bloklarını farklı erisim belirleyicileri ile bildirmek
mümkünmüdür?
C - 31 : Hayır, bir özelliğin belirtilen erisim belirleyicisi aynı zamanda hem get hem de set
bloklarınınn erisim belirleyicisidir. Fakat yapmak istediğinizi muhtemelen sadece get
bloğu olan yani readonly olarak bildirip set bloğunu private yada internal olan bir metot
yapacak sekilde gerçeklestirebilirsiniz.
S - 32 : Tek bir assembly de çoklu dil desteğini nasıl sağlayabilirim?
C - 32 : Bu su an için Visual Studio.NET tarafından desteklenen bir özellik değildir.
S - 33 : C# dizi türünden olan özellikleri destekliyor mu?
C - 33 : Evet, asağıda buna bir örnek verilmistir:
using System;
class Class1
{
private string[] MyField;
public string[] MyProperty
{
get { return MyField; }
set { MyField = value; }
}
}
class MainClass
{
public static int Main(string[] args)
{
Class1 c = new Class1();
string[] arr = new string[] {"apple", "banana"};
c.MyProperty = arr;
Console.WriteLine(c.MyProperty[0]); // "apple"
return 0;
}
}
S - 34 : Birden fazla assembly ile çoklu dil desteği sağlanabilirmi?
C - 34 : Malesef su an için IDE de bu desteklenmiyor. Bunu yapabilmek için komut
satırından projenizi /target:module argümanı ile derleyip modüllere ayırmanız gerekir. Ve
olusturduğunuz bu modülleri birlestirmek için yine komut satırından al(alink) aracını
çalıstırarak bu modüllerin birlestirilmesini sağlayın.
S - 35 : COM nesnelerine erismek için opsiyonel olan parametreleri nasıl simule
edebilirim?
C - 35 : Opsiyonel parametreler için System.Reflection altında bulunan Missing sınıfı
kullanılır. Her bir parametre için Missing.Value değeri kullanılabilir.
S - 36 : C++'taki varsayılan metot argümanlarının bir karsılığı C#'ta var mı?
C - 36 : Varsayılan argüman desteği yoktur ancak aynı etkiyi metot yükleme ile rahatlıkla
yapabilirsiniz.
Bu problem için metot yüklemeyi tercih etmemizin sebebi ileriki zamanlarda kaynak kodu
yeniden derlemeden varsayılan argümanı değistirme imkanı vermesidir. C++ taki
varsayılan argümanlar dersenmis kodun içine gömüldüğü için sonradan bu argümanı
kaynak kodu derlemeden değistirmek mümkün değildir.
S - 36 : Đçiçe geçmis bloklarda yada döngülerde hangi bloğun sonlandırdıldığını belirtmek
için kolay bir yol varmıdır?
C - 36 : Bu isin en kolay yolu goto atlama deyimini asağıdaki gibi kullanmaktır.
using System;
class BreakExample
{
public static void Main(String[] args)
{
for(int i=0; i<3; i++)
{
Console.WriteLine("Pass {0}: ", i);
for( int j=0 ; j<100 ; j++ )
{
if ( j == 10) goto done;
Console.WriteLine("{0} ", j);
}
Console.WriteLine("This will not print");
}
done:
Console.WriteLine("Loops complete.");
}
}
S - 37 : C#'ta deterministik sonlandırmayı nasıl sağlayabilirim.
C - 37 : GC mekanizması gerçek anlamda deterministik yapıda değildir. Yani ne zaman
gereksiz nesnelerin toplanacağı kesin olarak belilenmemistir. Bu yüzden kritik kaynaklara
sahip olan nesneleri tasarlamak için IDisposable arayüzünü uygulamıs sınıflar
tasarlamakta fayda vardır. Asağıdaki tasarım deseni sınıf için ayrılan kaynağın blok
sonunda bırakılacağını bildirmektedir.
using(FileStream myFile = File.Open(@"c:\temp\test.txt", FileMode.Open))
{
int fileOffset = 0;
while(fileOffset < myFile.Length)
{
Console.Write((char)myFile.ReadByte());
fileOffset++;
}
}
Akıs, using bloğunun sonuna geldiğinde myFile nesnesi üzerinden Dispose() metodu
çağrılacaktır. Nesneleri bu sekilde using ile kullanabilmek için ilgili sınıfın IDisposable
arayüzünü uygulaması gerektiğini unutmayın.
S - 38 : C#'ta metotlar için değisken sayıda argüman (vararg) desteği varmıdır?
C - 38 : params anahtar sözcüğü bir metodun değisken sayıda parametre alabileceğini
belirtir. Metot bildiriminde params anahtar sözcüğünden sonra herhangi bir metot
parametresi bildirilemez. Ve bir metot için sadece bir tane params anahtar sözcüğünün
kullanımına izin verimistir. Asağıda params'ın kullanımıa bir örnek verilmistir.
using System;
public class MyClass
{
public static void UseParams(params int[] list)
{
for ( int i = 0 ; i < list.Length ; i++ )
Console.WriteLine(list[i]);
Console.WriteLine();
}
public static void UseParams2(params object[] list)
{
for ( int i = 0 ; i < list.Length ; i++ )
Console.WriteLine((object)list[i]);
Console.WriteLine();
}
public static void Main()
{
UseParams(1, 2, 3);
UseParams2(1, 'a', "test");
int[] myarray = new int[3] {10,11,12};
UseParams(myarray);
}
}
Çıktı
1
2
3
1
a
test
10
11
12
S - 39 : C#'ta string türünden bir değiskeni int türüne nasıl dönüstürebilirim?
C - 39 : Asağıda bu duruma bir örnek verilmistir.
using System;
class StringToInt
{
public static void Main()
{
String s = "105";
int x =
Convert.ToInt32(s);
Console.WriteLine(x);
}
}
S - 40 : C# ile yazılmıs uygulamalardan çıkmak için exit() gibi bir fonksiyon varmıdır?
C - 40 : Evet, uygulamadan çıkmak için System.Environment.Exit(int exitCode)
metodunu yada uygulamanız bir windows uygulaması ise Aplication.Exit() metotlarını
kullanabilirsiniz.
S - 41 : Bütün assembly için özel bir nitelik nasıl belirtilir?
C - 41 : Global nitelikler en tepedeki using deyiminden sonra ve herhangi bir sınıf yada
isim alanı bildiriminden önce yapılmalıdır. Örnek :
using System;
[assembly : MyAttributeClass]
class X {}
Not : IDE tarafından yaratılan projelerde bu nitelik bildirimleri AssemblyInfo.cs
dosyasında yapılmıstır.
S - 42 : C# ile yazılmıs kodu klasik COM istemcilerinin kullanımına sunmak için nasıl
kayıt(register) etmeliyim?
C - 42 : regasm aracını kullanarak eğer gerekliyse type library leri olusturun. Sınıf
windows registery ye kayıt edildikten sonra bu sınıfa COM istemcileri tarafından sanki bir
COM bileseniymis gibi erisilebilir.
S - 43 : Birden fazla derlenecek kaynak kod aynı anda derlendiğinde, çalıstırılabilir
dosyanın ismi nasıl belirleniyor?
C - 43 : Çalıstırılabilir dosyanın adı Main metodunun bulunduğu kaynak dosyanın adı ile
aynı olur. Komut satırından /out argümanı ile çalıstırılabilir dosyanın adını kendiniz de
belirleyebilirsiniz. Örneğin:
C:\ csc /out:Uygulama.exe dosya1.cs dosya2.cs
komutu çalıstırılabilir dosyanın adını Uygulama.exe yapacaktır.
S - 44 : C#'ta String türünden nesneler nasıl karsılastırılır?
C - 44 : Geçmiste iki stringi == ve != operatörleri ile karsılastırmak için ToString()
metodu kullanılmalıydı. Su anda ise yine bu yöntem geçerli olmasına rağmen string ler
== ve != operatörü ile karsılastırıldıklarında değiskenlerin referansları yerine onların
değerleri karsılastırılır.
Eğer gerçekten string değiskenlerinin referanslarını karsılastırmak istersek asağıdaki kodu
kullanabiliriz.
if ((object) str1 == (object) str2) { ... }
Asağıda string değiskenlerinin nasıl çalıstığına bir örnek verilmistir.
using System;
public class StringTest
{
public static void Main(string[] args)
{
Object nullObj = null;
Object realObj = new StringTest();
int i = 10;
Console.WriteLine("Null Object is [" + nullObj + "]\n" +
"Real Object is [" + realObj + "]\n" +
"i is [" + i + "]\n");
// Show string equality operators
string str1 = "foo";
string str2 = "bar";
string str3 = "bar";
Console.WriteLine("{0} == {1} ? {2}", str1, str2, str1 == str2 );
Console.WriteLine("{0} == {1} ? {2}", str2, str3, str2 == str3 );
}
}
Çıktı
Null Object is []
Real Object is [StringTest]
i is [10]
foo == bar ? False
bar == bar ? True
S - 45 : C++'taki typedef komutunun yaptığı gibi farklı türler için takma isimleri
kullanılabilirmi?
C - 45 : Tam olarak değil ama bir dosyada using deyimini asağıdaki gibi kullanarak
herhangi bir türe takma isim verebilirsiniz.
using System;
using Integer =
System.Int32; // takma
isimi
using deyiminin kullanımı hakkında ayrıntılı bilgi için C# standartlarını incelyin.
S - 46 : C#'ta sınıf ile yapı arasındaki farklar nelerdir?
C - 46 : Sınıflar ve yapılar arasındaki farkların listesi oldukça fazladır. Yapılar sınıflar gibi
arayüzleri uygulayabilir ve aynı üye elemanlara sahip olabilirler. Yapılar, sınıflardan bir
çok önemli noktada ayrılır; yapılar değer tipleri sınıflar ise referans tipleridir ve türetme
yapılar için desteklenmez. Yapılar stack bellek bölgesinde saklanır. Dikkatli programcılar
bazen yapıları kullanarak uygulamanın performansını artırabilirler. Mesela Point yapısı için
sınıf yerine yapı kullanmak çalısma zamanında tahsis edilen bellek açısından oldukça
faydalıdır.
S - 47 : Bir karakterin ASCII kodunu nasıl elde edebilirim?
C - 47 : char türden değiskeni int türüne dönüstürürseniz karakterin ASCII kodunu elde
edersiniz.
char c = 'f';
System.Console.WriteLine((int)c);
yada bir string deki herhangi bir karakter için
System.Console.WriteLine((int)s[3]);
kodunu kullanabilirsiniz.
.NET kütüphanesindeki Convert ve Encoding sınıflarının yardımıylada bu islemi
gerçeklestirmek mümkündür.
S - 48 : Versiyonlama bakıs açısıyla arayüz türetmenin sınıf türetmeye karsı getirileri
nelerdir?
C - 48 : Versiyonlama bakıs açısıyla arayüz türetmenin sınıf türetmesine göre daha az
esenek olduğunu söylemek mümkündür.
Sınıflarda farklı versiyonlara yeni üye elemanlar örneğin yeni metot eklemeniz
mümkündür. Bu metot abstract olmadığı sürece yeni türetilen sınıflar bu metodun
fonksiyonalitesine sahip olacaktır. Arayüzler uygulanmıs kodların türetilmesini
desteklemediği için bu durum arayüzler için geçerli değildir. Bir arayüze yeni bir metot
eklemek sınıflara yeni bir abstract metot gibidir. Bu arayüzü uygulayan bir sınıf bu
metodu aynen uygulayıp kendine göre anlamlandırmalıdır.
S - 49 : C# koduna inline assembly yada IL kodu yazmak mümkünmüdür?
C - 49 : Hayır.
S - 50 : Bir metodu yada herhangi bir üye elemanının kullanımını sadece belirli bir isim
alanı için sınırlayabilirmiyiz?
C - 50 : Đsim alanları için bir kısılama yapılamaz çünkü isim alanları koruma amaçlı olarak
kullanılmamaktadır ancak internal erisim belirleyicisi ile bir türün sadece ilgili aseembly
dosyası içinde kullanılabilecek durumuna getirebiliriz.
S - 51 : try bloğu içerisinde return ile metodu sonlandırısam finally bloğundaki kodlar
çalıstırılır mı?
C - 51 : Evet, finally bloğundaki kodlar siz return ile metodu sonlandırsanızda try
bloğunun sonuna gelsenizde her zaman çalısacaktır. Örneğin :
using System;
class main
{
public static void
Main()
{
try
{
Console.WriteLine("In
Try block");
return;
}
finally
{
Console.WriteLine("In
Finally block");
}
}
}
programında hem "In try block" hemde "In Finally block" yazıs ekrana yazdırılacaktır.
Performans açısından return sözcüğünü try bloğunda yada finally bloğundan sonra
kullanmanın bir farkı yoktur. Derleyici yukarıdaki durumda return ifadesinin sanki finally
bloğunun dısındaymıs gibi davranır. Eğer yukarıda olduğu gibi return deyimi herhangi bir
ifade ile kullanılmıyorsa her iki durumdada IL olarak üretilen kodlar aynıdır. Fakat eğer
return deyimi bir ifade ile kullanılıyorsa try bloğundaki return ifadesinde ekstradan store
ve load deyimlerinin IL de olacağı açıktır.
S - 52 : C# try-catch-finally vloklarını destekliyormu?
C - 52 : Evet destekliyor, asağıda bu blokların kullanımına bir örnek verilmistir.
using System;
public class TryTest
{
static void Main()
{
try
{
Console.WriteLine("In Try block");
throw new ArgumentException();
}
catch(ArgumentException n1)
{
Console.WriteLine("Catch Block");
}
finally
{
Console.WriteLine("Finally Block");
}
}
}
Çıktı
In Try Block
Catch Block
Finally Block
S - 53 : Statik indeksleyici tanımlamak mümkünmüdür?
C - 53 : Hayır. Statik indeksleyici tanımalamaya izin verilmemistir.
S - 54 : Derleyiciyi /optimize+ argümanı ile çalıstırdığımızda ne gibi optimizasyonlar
yapar.
C - 54 : C# derleyicisini yazan takının bu soruya verdiği cevap:
Kullanılmayan lokal değiskenleri atıyoruz. (örnek, hiç okunmayan lokal değiskenler -
kendisine değer verilmis olsa bile-).
Hiç bir sekilde erisilemyecek(unreachable) kodları atıyoruz.
try bloğu bos olan try/catch bloklarını kaldırıyoruz.
try bloğu bos olan try/finally bloklarını kaldırıyoruz.(normal koda çevrlir)
finally bloğu bos olan try/finally bloklarını kaldırıyoruz.(normal koda çevrlir)
Dallanmalarda diğer dallanmaları optimize ediyoruz. Örneğin
gotoif A, lab1
goto lab2:
lab1:
kodu
gotoif !A, lab2
lab1:
koduna dönüstürülür.
We optimize branches to ret, branches to next instruction, branches to branches.
Dallanmaları "ret"'e, "next instruction" lara veya diğer "branch" lara dönüstürüyoruz.
S - 55 : C# ile registry'ye nasıl erisebilirim?
C - 55 : Microsoft.Win32 isim alanındaki Registry ve Registry sınıflarını kullanarak bu
alana erismek mümkündür. Asağıdaki program bir registry anahtarını okuyup değerini
yazdırmaktadır.
using System;
using Microsoft.Win32;
class regTest
{
public static void Main(String[] args)
{
RegistryKey regKey;
Object value;
regKey = Registry.LocalMachine;
regKey =
regKey.OpenSubKey("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
value = regKey.GetValue("VendorIdentifier");
Console.WriteLine("The central processor of this machine is: {0}.", value);
}
}
S - 56 : C# global sabitleri tanımlamak için #define komutunu destekler mi?
C - 56 : Hayır. Eğer C dilindeki asağıdaki koda benzer bir kullanım elde etmek istiyorsanız
#define A 1
bu kodu kullanabilirsiniz.
class
MyConstants
{
public
const int A
= 1;
}
Böylece A makrosuna her erismek istediğinizde MyConstants.A seklinde bir kullanıma
sahip olursunuz.
MyConstants.A seklindeki kullanım ile 1 sayısının kullanımı arasında bir fark yoktur. Yani
aynı kod üretilecektir.
S - 57 : Yeni bir proses çalıstırıp bu proesisin sonlanmasını nasıl bekleyebilirim?
C - 57 : Asağıdaki kod argüman olarak verilen çalıstırılabilir programı çalıstırı ve çalısan
bu programın kapatılması için bekler.
using System;
using System.Diagnostics;
public class ProcessTest {
public static void Main(string[] args) {
Process p = Process.Start(args[0]);
p.WaitForExit();
Console.WriteLine(args[0] + " exited.");
}
}
S - 58: Bir metot obsolete olarak asıl isaretlenir?
C - 58 : using System; yazdığınızı varsayarak
[Obsolete]
public int Foo() {...}
yada
[Obsolete("Bu mesaj metodun neden Obsolete olduğunu açıklar.")]
public int Foo() {...}
Not: Obsolete kelimesindeki O harfi büyüktür.
S - 59: using deyimini kaynak koduma eklememe rağmen derleyici tanımlanmamıs
türlerin bulunduğunu söylüyor. Nerede yanlıs yapıyorum acaba?
C - 59 : Büyük bir ihtimalle isim alanının bulunfuğu assembly dosyasını referans vermeyi
unutmussunuzdur. using deyimi sadece bir sentaksdır. Assembly nin fiziksel olarak
konumunu da ayrıca belirtmeniz gerekir. IDE yi kullanarak project menüsünden add
reference seöeneği seçip istediğiniz assembly ye referans verebilirsiniz. Komut satırı
derleyicisi kullanıyorsanız /r argümanını kullanmalısınız.
S - 60 : Basit bir çok kanallı uygulama için örnek kod var mı?
C - 60 : Evet. örnek :
using System;
using System.Threading;
class ThreadTest
{
public void runme()
{
Console.WriteLine("Runme Called");
}
public static void Main(String[] args)
{
ThreadTest b = new ThreadTest();
Thread t = new Thread(new ThreadStart(b.runme));
t.Start();
}
}
S - 61: Override edilmis bir metodun temel sınıftaki versiyonunu nasıl çağırabilirim?
C - 61 : Asağıdaki gibi base anahtar sözcüğünün kullanarak çağırabilirsiniz.
public class MyBase
{
public virtual void meth()
{
System.Console.WriteLine("Test");
}
}
public class MyDerived : MyBase
{
public override void meth()
{
System.Console.WriteLine("Test2");
base.meth();
}
public static void Main()
{
MyDerived md = new MyDerived();
md.meth();
}
}
S - 62: C# gelistiricilerini düzenli ifadeler(regex) desteği sunulmusmudur?
C - 62 : Evet, .NET sınıf kütüphanesi programcılara düzenli ifadelerle çalısmak için
System.Text.RegularExpressions isim alanında bir takım sınıflar sağlamaktadır.
S - 63 : C# ile yazmıs olduğum uygulamayı çalıstırdığımda neden güvenlik hatası
alıyorum?
C - 63 : Bazı güvenlik hataları ağ üzerinde paylasıma açılmıs kaynaklar üzerinde
çalısırken alnır. Roaming profilleri, mapped diskler gibi kaynaklar üzerinde çalısmayan
bazı sınıflar vardır. Bunun olup olmadığını kontrol etmek için uygulamanızı lokal diskinize
alıp yeniden çalıstırmayı deneyin.
Bu tür durumlarda genellikle System.Security.SecurityException istisnai durumu
meydana gelir.
Bu tür sorunların üstesinden gelmek için caspol.exe aracı yardımıyla intranet için güvenlik
policy nizi codegroup 1.2 ye ayarlayabilirsiniz.
S - 64: try-catch bloklarında faaliyet alanı (scope) problemlerinin üstesinden nasıl
gelirim?
C - 64 : try bloğu içinde yarattığınız nesneye catch bloğu içinden erisemezsiniz çünkü try
bloğunun sonunda ilgili nesnenin faaliyet alanı bitecektir. Bunun önüne geçmek için
asağıdaki kod bloğu kullanılabilir.
Connection conn = null;
try
{
conn = new Connection();
conn.Open();
}
finally
{
if (conn != null) conn.Close();
}
try bloğundan önde değiskeni null değere atamakla derleyicinin CS0165 (Use of possibly
unassigned local variable 'conn' ) hatasını vermesini engellemis oluruz.
S - 65: .NET gelistirme ortamında regsvr32 ve regsvr32 /u komutlarının karsılığı nedir?
C - 65 : RegAsm aracını kullanabilirsiniz. .NET SDK içinde bu aracın kullanımı hakkında
detaylı bilgiyi bulmanız mümkündür.
S - 65: C#, parametreleri özellikleri destekliyor mu?
C - 65 : Hayır, fakat dilin temel yapısında indeksleyici diye ayrı bir kavram vardır.
Bir indeksleyici bir türün dizi gibi indek operatörü ile kullanılabilmesini sağlar.Kısaca
özellikler field benzeri erisimi indeskleyiciler ise dizi benzeri erisimi sağlarlar.
Örnek olması açısından daha önce yazdığımız Stack sınıfını düsünün. Bu sınıfı tasarlayan
sınıfın üye elemanlarına bir dizi gibi erisilmesini isteyebilir ve böylece gereksiz Pop ve
Push çağrımları yapılmamıs olur. Yani stack bir bağlı liste gibi tasarlanmıs olmasına
rağmen bir dizi gibi kullanılabilmektedir.
Đndeksleyici bildirimi özellik bildirimine benzemektedir. Đki bildirim arasındaki en büyük
fark indeksleyicilerin isimlerinin olmamasıdır.(indeskleyici bildiriminde isi yerine this
anahtar sözcüğü kullanılır.) Diğer bir fark ise indekleyicilerin indeks parametresi
alabilmesidir. Bu indeks parametresi köseli parantezler içinde yazılır.
C# ile XMLQuery Örneği
Bu yazımda sizlere, XML dokümanlarında nasıl sorgulama yapabileceğimizi basit bir örnek ile
anlatmaya çalısacağım.
Dilerseniz hemen uygulamaya geçelim. Yeni bir Asp.Net Web Application açın ve adını XmlQuery
olarak ayarlayın. Ardından projeye yeni bir xml doküman ekleyin ve adını Kayıtlar.Xml olarak
ayarlayın. Xml dokümanın yapısını asağıdaki gibi düzenleyin.
<?xml version="1.0" encoding="utf-8" ?>
<Kayitlar>
<Kayit id="1" tip="A">
<Adi>Tolga</Adi>
< Soyadi>Güler</Soyadi>
<Numarası>1544747</Numarası>
</Kayit>
<Kayit id="2" tip="B">
<Adi>Utku</Adi>
<Soyadi>Selen</Soyadi>
<Numarası>4577877</Numarası>
</Kayit>
<Kayit id="3" tip="B">
<Adi>Murat</Adi>
<Soyadi>Kula</Soyadi>
<Numarası>8787878</Numarası>
</Kayit>
<Kayit id="4" tip="C">
<Adi>Argun</Adi>
<Soyadi>Çelikten</Soyadi>
<Numarası>7454621</Numarası>
</Kayit>
</Kayitlar>
WebForm1.aspx.cs dosyasına asağıdaki kodları ekleyin.
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Xml;
namespace XmlQuery
{
public class WebForm1 : System.Web.UI.Page
{
private void Page_Load(object sender, System.EventArgs e)
{
XmlNodeList Isimler,Isimler2,Isimler3,Isimler4,Isimler5;
//XmlNodeList türinden değiskenlerimizi tanımlıyoruz.
XmlTextReader rdr = new XmlTextReader("http://localhost/XmlQuery/kayitlar.xml");
// XmlTextReader sınıfı yardımı ile xml dökümanına erisiyoruz.
XmlDocument MyXmlDoc = new XmlDocument();
MyXmlDoc.Load(rdr);
//XmlDocument sınıfını xml dökümanı üzerinde islem yapabilmek için kullanıyoruz
// Xml domüanından id si 1 olan isimleri seçmek için
Isimler = MyXmlDoc.SelectNodes("/Kayitlar/Kayit[@id='1']/Adi");
/* XmlDocumen.SelectNodes metoduna parametre olarak verdiğimiz XPATH
e dikkat edin.
*/
for(int i = 0;i < Isimler.Count;i++)
Response.Write(Isimler.Item(i).InnerXml.ToString()+"<br>");
// Sonuç "Tolga" olacaktır.
// id si 1 veya 2 olan kayıtlar için
Isimler2 = MyXmlDoc.SelectNodes("/Kayitlar/Kayit[@id='1' or @id='2']/Adi");
for(int i = 0;i < Isimler2.Count;i++)
Response.Write(Isimler2.Item(i).InnerXml.ToString()+"<br>");
// Sonuç "Tolga" ve "Utku" olacaktır.
// id si 1 ve tipi A olan kayıtlar için
Isimler3 = MyXmlDoc.SelectNodes("/Kayitlar/Kayit[@id='1' and @tip='A']/Adi");
for(int i = 0;i < Isimler3.Count;i++)
Response.Write(Isimler3.Item(i).InnerXml.ToString()+"<br>");
// Sonuç "Tolga" olacaktır.
// tipi B olan kayıtların adının ilk iki harfi "Ut" olanlar
Isimler4 = MyXmlDoc.SelectNodes("/Kayitlar/Kayit[@tip='B']/Adi[substring(.,1,2) ='Ut']");
for(int i = 0;i < Isimler4.Count;i++)
Response.Write(Isimler4.Item(i).InnerXml.ToString()+"<br>");
// Sonuç "Utku" olacaktır.
// tipi B olan kayıtların adında "ura" geçenler
Isimler5 = MyXmlDoc.SelectNodes("/Kayitlar/Kayit[@tip='B']/Adi[contains(.,'ura')]");
for(int i = 0;i < Isimler5.Count;i++)
Response.Write(Isimler5.Item(i).InnerXml.ToString()+"<br>");
// Sonuç "Murat" olacaktır.
}
}
}
Siz örnekleri istediğiniz gibi gelistirip çoğaltabilirsiniz.
2003 – 2005 Microsoft Yazılım Gelistirme Araçları Yol Haritası – 1
Subat 2002’de Visual StudioNet ve dotNET Platformu dünyadaki tüm yazılım
gelistiricilerin hizmetine sunuldu; bu önemli olay sayesinde programcılar çok değisik
alanlarda program gelistirme islerini dotNET platfromu ve Visual StudioNET ile yapabilir
hale geldi. Visual Studio.NET 2003 ile programcılar müsterilerine basarısı kanıtlanmıs,
yüksek performanslı ve güvenilir yazılımlar gelistirmeye devam ediyorlar.
Đs dünyasındaki değisikliklerle birlikte ihtiyaç duyulan yazılımların da gelismesi ve
değismesi gerekiyor. Böyle bir ortamda Micrsoft kendisinin yazılım gelistirme araçlarını
kullanan gelistiricilere devrim niteliğinde ve is dünyasının değisen ihtiyaçlarına en kısa ve
en iyi çözümlerini üretecek yazılım gelistirme araçlarını sunmaya devam
ediyor. Kurumların gelecekteki yazılım ihtiyaçlarının planlamasını yaparken onlara
yardımcı olmak amacıyla Microsoft bu yol haritasını sunmaktadır. Bu belge özellikle su
ürünler üzerinde yoğunlasmıstır:
• Microsoft Ofis 2003 için Visual Studio araçları: Su anda beta asamasında
olan bu teknoloji sayesinde, Microsoft Office Word 2003 ve Microsoft Excel 2003’ü
.Net ortamında programlayabileceğiz.
• “Whidbey” kod adlı Visual Studio 2004: Visual Studio.NET ve .NET
platformunun bu versiyonunda birçok yenilikler ve değisikliklerle geliyor. Baslıca
yenilikler sınıf kütüphanesinde, ortak dil çalısma (CLR) kısmında, programlama
dillerinde ve Visual studio.NET’in arayüzünde (IDE) olacaktır. Ayrıca SQL Server’ın
yeni versiyonu olan SQL Server “Yukon” ile büyük bir entegrasyon sağlanacaktır.
Bu sayade C# ve Visual Basic.Net ile saklı yordamları (stored procedures) yazıp
Yukon üzerinde çalıstırabileceğiz.
• “Orcas” kod adlı Visual Studio 2005: Bu versiyonda ise “Longhorn “ isimli
Windows isletim sistemiyle daha iyi entegrasyon ve programlama alt yapısı
sağlanacak.
Microsoft yazılım gelistirme araçları her zaman Windows platformunun en son
özelliklerine erismeyi ve onları programlamayı programcılara sunmustur. Yukarıda da
görüldüğü gibi Microsoft bu geleneği sürdürmeye devam edecektir. Bu bağlamda
Microsoft Ofis Sistem 2003’ü, SQL Server Yukon’u ve Windows isletim sistemlerini
programlamak için bir çok kolaylıklara sahip olacağız biz yazılım gelistiriciler olarak.
Microsoft Ofis 2003 için Visual Studio Araçları
“Yazılım gelistiriciler hem Visual Studio hem de Microsoft’un basarısındaki öncül güç
olmuslardır.”
- Eric Rudder, Sunucu ve Araçlardan sorumlu Genel baskan yardımcısı.
Visual Studio 2003’ün hemen ardından Microsoft, Ofis 2003 için Visual Studio araçlarını
piyasaya sürdü. Bu yeni teknoloji sayesinde .NET platformundan yönetilen kod sayesinde
Microsoft Word 2003 ve Microsoft Excel 2003 için kod yazılabilecek. Tıpkı VBA ve COM
tabanlı otomasyon projeleri gibi. Microsoft Ofis 2003 için Visual Studio Araçları biz
yazılımcılara su önemli avantajları da getiriyor:
• Tanıdık programlama deneyimi: Microsoft Ofis 2003 için Visual Studio Araçları
ile programcılar .Net sınıf kütüphanelerini kullanabilirler. Böylelikle bir çok
zahmetli is için çok daha az satır kod yazmak zorunda kalacağız. Mesala stringleri
islemede, veri yapılarında, veri tabanı islemlerinde ve dosya yönetiminde büyük
kolaylıklar sağlar. Dahası Visual Studio.NET ile daha güçlü ofis uygulamaları
gelistirme sansına da sahibiz. Microsoft Ofis 2003 için Visual Studio Araçları ile
Word ve Excel dosyalarının nesne modellerine tam olarak erisim ve onları
programlama hakkımız doğuyor.
• Kolaylastırılmıs program kurulumu ve bakımı: Microsoft Ofis 2003 için Visual
Studio Araçları ile yazdığımız kodlar DLL olarak derlenebilir. Bu DLL(ler) genelde
ağ üzerinde paylasımda olan bir yerde dururlar ve Excel veya Word açıldığında
ilgili dll makinaya indirilir ve çalıstırılır. Eğer kodda bir değisiklik olursa yeni
derlenmis kod otomatik olarak istemci makineye indirilir.
• Gelismis güvenlik: Microsoft Ofis 2003 için Visual Studio Araçları ile daha güvenli
bir çalısma ortamına sahip olacağız. Hem güvenlik kod (trusted code)
çalıstıracağız hem de güvenliğin sistem yöneticisi tarafından denetim altına
alınması sağlanacak.
“Whidbey” kod isimli Visual Studio 2004
“ Gelismis araçlar, tüm kritik zamanlarda, uygulamar için çok önemli dönemeçler
olmustur Aynı sekilde uygulamardaki bu kritik dönemeçler bilgi islem alanında bir sonraki
asamayı getirmistir.”
-Bill Gates
2004 yılında piyasaya sunulacak olan Visual Studio.NET ve .NET altyapısı yazılım
gelistirmenin tüm alanlarında çok önemli değisiklikleri beraberinde getirecektir.
Gelistiricilerden alınan geribildirimler (feedback) ve bunların dikkatlice
değerlendirilmesiyle programcıların daha verimli olmalarını ve IDE içinden diğer yazılım
gelistiricilere ulasmayı ve destek hizmetlerine ulasmayı mümkün kılacaktır. Yenilikler
programlama dillerindeki gelismeler, .NET Platformundaki değisiklikler ve kurumsal
yazılım gelistirme projelerine destek ve yardımların artırılmasıdır.
Diğer göze çarpan gelisme ise Microsoft tarafından üretilen yazılım gelistirme araçlarının
planlı olarak birbiri ile ve sistemle daha uyumlu hale gelmesidir. Whidbey’in SQL Server
Yukon ile çok iyi entagrasyonu bu uyumluluk planlarının basında geliyor. Tıpkı Windows
Server 2003’ün daha sisteminize kurulurken .NET Plaformunun varsayılan olarak
kurulması gibi. Bu sayede SQL Server Yukon CLR ortamına tam olarak adapte olmus hale
gelecektir. Yukarıda da belirtildiği gibi Whidbey ortamında SQL Server Yukon üzerinde
çalısan saklı yordamlar (stored procedures) yazabileceğiz. Tabi ki Whidbey ile veri tabanı
islemlerimizi daha az kod yazarak gerçeklestirme sansımız vardır.
Yukarıdaki genis değisikliklerin yanınnda yenilikler baslıca su konularda olmustur:
• Programlama Dilleri: Bu versiyonda Microsoft Visual Studio içerisinde tam
destek verdiği 4 dilde (Visual Basic, Visual C#, Visual C++ ve Visual J#) önemli
değisiklikler yapacak. Bu değisikler dillerin güçlerini artıracakları gibi dillerin
özellikleri ve ortak çalısabilmesine en ufak bir yan etkisi olmayacaktır.
• .NET Platformu: Whidbey ile .NET Platformundaki sınıf kütüphanelerinde önemli
değisiklikler olacak. Değisiklikler daha güçlü ve hos Windows uygulamaları
gelistirmeyi sağlayacağı gibi ASP.NET programlama ve ADO.NET veri islemleri
daha verimli olacaktır. Ayrıca en son web servisleri standartlarını destekleyecek ve
daha genis çaplı cihaz tabanlı (Mobil veya diğer programlanabilir cihazlar için)
programlama imkanları gelecek.
• Kurumsal Yazılım Gelistirme: Bu yeni versiyon ile sistem tasarımcılarına ve
kurumsal yazılım gelistiren yazılım mühendislerine kapsamlı ve etkili çözümler için
yeni araçlar sunulacak. Bu araçlar gelitirilmis proje analizi ve tasarımı, yazılım
ayarları yönetme ve yazılımın dağıtılması (deployment) gibi kritik noktar için
düsünülmüstür.
Programlama Dilleri
.NET Platforumunda yazılım gelistirmek için 20’den fazla değisik dil kullabiliriz. Bunun
yanında Microsoft resmi olarak .Net platformunda 4 dili Whidbey’de destekliyor olacak.
Microsoft Whidbey’de bu 4 dil için gerekli tüm araçları ve desteği en güvenilir yazılım
gelistirmek için bizlere sunuyor.
Visual Basic
Whidbey ile gelecek olan Visual Basic versiyonunda programcıların verimliliğini inanılmaz
seviyede artıracak yenilikleri göreceğiz. Tabi bu yenilikler Visual Basic programlama dili
ile .NET ortamında yazılım gelistirmek için bize sunulan tüm özellikleri de sonuna kadar
kullanacağız. Visual Basic Whidbey’deki kritik değisiklikler temel olarak sunlardır:
1
Sık sık yazmak zorunda kaldığımız bazı kodları yazmak çok daha hızlı
olacaktır.
2
Program tasarım halindeyken dahi hataları minimize etmek için alınan
önlemler ve yollar.
3 Veri ve Veritabanlarına daha kolay erisim.
4 Gelistirilmis RAD hata ayıklama
5 Çok ileri seviyede Visual Basic programları yazabilme.
1. Çoğu programda sık sık yazmak zorunda kaldığımız kodların yazımı Visual Basic
Whidbey’de en az iki katı hızlı bir biçimde yazılabilinecek. Programcı verimliliğin artması
için çalısma zamanı nesnelerine ve metodlarına direk olarak erisim ve bunları getirdiği
esneklik diğer bir güzel haber. Kod editöründeki gelismeler sayesinde sık sık yazılan
kodları hızlıca yazmak için sadece belirli boslukları doldurmak yetecektir. Bu sayede dilin
söz dizimi yerine gelistirilen projenin mantığı üzerinde yoğunlasma fırsatı bulacağız.
2. Yeni kod editörü sayesinde her seviyedeki programcıların hatalarını en aza, daha
tasarım asamasında, indirmek mümkün. Microsoft Word’ta bulunan gramer ve yazım
hatalarını kontrol ve düzeltmeye yarayan aracın bir benzeri Visual Basic Whidbey ile
gelecek. Visual Basic derleyicisi de daha iyi bir kod denetimi yaptıktan sonra programı
derleyecek böylece çalısma anında ortaya çıkması muhtemel hataların önüne geçilecek.
3. Visual Basic Whidbey ile veriye erisim ve veri üzerinde değisiklikler yapmak çok daha
kolay hale geliyor. Kolaylastırılan islerin basında, yerel ve uzaktaki veriye, isle ilgili veri
tasıyan nesnelere ve uzaktaki XML Web servislerine erisim geliyor. Whidbey ayrıca
sadece veriler üzerinde çalısan (databound) programlar gelistirmeyi de inanılmaz kolay
hale getiriyor. Bu tür programları tek satır dahi kod yazmadan dahi gelistirme imkanı
bulacağız. Çok sık kullanılan veriye erisim senaryoları için tasarlanan bu yöntemlerle
programları veri kaynağındaki tabloları ve sütunları sürükleyip bırakarak programı
gelistirebileceğiz.
4. Whidbey ile gelen hata ayıklama yöntemleri için araçlar hem daha güçlü hem de Visual
Basic programcılarının asina oldukları bir biçimde tasarlandı. Edit ve Continue
komutlarının bastan tasarımı sayesinde programda hata ayıklarken tekrar tekrar
programı derlemeyi ve hata ayıklamaya devam etmeyi unutun. Ayrıca break modundaki
değisiklikler ile daha önce görülmemis en güçlü ve esneklikte hata ayıklama araçlarına
sahip olacağız.
5. Son olarak, ileri seviyedeki Visual Basic programıları için dilde bir çok iyilestirmeler
yapıldı. Bunlar isleslere asırı yüklenme (operator overloading), isaretsiz veri tipleri
(unsigned data types), kod içinde XML tabanlı kod dokümantasyonu yazımı (inline XMLbased
code doumentation) ve kısmi veri tipleri (partial types). Dildeki bu gelismeler
sayesinde Visual Basic programcıları tip güvenli (type -safe), yüksek performanslı,
derleme zamanında onaylanmıs (compile time-verified) olan generics yazabilecekler. Bu
sayede kodun tekrar tekrar faklı veri tipleriyle birlikte kullanılmasını beraberinde
getirecektir.
Önceki versiyonları gibi Visual Basic Whidbey’de hızlı bir biçimde program gelistirmeyi
mümkün kılmak üzerine yoğunlasmıstır. Planlı olan yenilikler ile Visual Basic Programcıları
daha güvenli, daha sağlam ve daha hos programları kolay bir biçimde gelistirip onları
aynı kolaylıkla web, çalısma grubu ve kurumsal ortamlarda dağıtmayı/kurmayı
garantiliyor.
Visual C++
Visual C++ Whidbey önceki versiyonunlarından daha güçlü olarak sistem programlama
ve yazılım gelistirme görevlerini hem Windows hem de .NET’i tercih eden programcıları
hedef alıyor. Planlı olarak yapılan yenilikler derleyiciyi, gelistirme ortamını, programlama
dilini ve temel kütüphaneleri kapsıyor. Ek olarak Visual C++ Whidbey ile mobil cihazlar
için native C++ uygulamarında gelistirmek mümkün olacak.
C++ derleyicisindeki gelismelerden biri Profile Gudied Optimization (POGO)’dır. POGO
teknolojisi derleyicinin bir uygulamayı inceleyip onun nasıl kullanıldığı hakkında bilgi
toplamasıdır. Bu bilgiler ile Visual C++ kodu daha iyi biçimde optimize edecek. Son hali
olmasada 64-Bit POGO teknolojisinin Pre-release versiyonu ücretsiz olarak indirilebilir.
Whidbey de ise bu teknoloji daha gelismis olarak 32-bit derleyici için hazır olarak
gelecektir.
CLR’nin ön sürümlerinde Visual C++ Managed Extentions ile gelecek ve programcılar
.NET’in tüm tüm olanaklarına ulasabilecekler. Whidbey sürümünde ise Visual C++
gelistiricileri C++’a has özelliklere, mesela generics'e sahip olacak. Diğer gelismeler ile
C++’ı CLR ortamında yazılım gelistirme aracı olarak kullanmak daha kolay bir hal
alacaktır.
Visual C++ Whidbey C++ temel kütüphanelerinde bir çok gelismeyi beraberinde
getiriyor. Bildiğimiz gibi C++’ta kullanabileceğimiz dünya çapında yaygın kütüphaneler
bunuluyor. Bunlar en çok öne çıkanlarından biri de Microsoft Foundation Class
(MFC)’dir. Visual C++ Whidbey ile gelen MFC’de bir yönden yeni gelismeler olacak.
Bunların en dikkat çekeni ise Windows Fusion teknolojisine destektir. Windows Fusion
DLL’lerin çıkardığı sorunları aza indirmek için yaratılan ileri seviye bir teknolojidir. Diğer
önemli gelisme ise kolayca MFC tabanlı uygulamaların .NET platformu tarafından
desteklenmesidir.
Viusal C#
Microsoft Visual C#’a değisik dillerden çok hos özellikleri Whidbey’de eklemeyi planlıyor.
Bu değisiklikler ile programcılara “Kod odaklı RAD” olanakları sağlanacak. Yani, C#
programcıları daha verimli bir biçimde tekrar kullanılabilir nesne yönelimli bilesenler ve is
taslakları gelistirecekler. Eklenecek yenilikler generics, itaretörler, anonymous metodlar
ve kısmi tiplerdir.
Bir yazılım projesinin karmasıklığı artıkça programcılar daha fazla oranda hazır olan
program bilesenlerini direk kullanmaya veya onların özerinde az bir değisiklikle kullanma
eğilimi gösterirler. Böyle yüksek seviyede kodun yeniden kullanılmasını basarmak için
generics ismi verilen yöntemi tercih ederler. Whidbey’de CLR içine yerlestirilen özellikler
sayesinde yüksek perfromanslı, tip güvenli ve derleme zamanında onaylanmıs
generics'leri C++’ta bulunan template’lere benzer biçimde gelistirebiliriz. Generic ler
programcılara kodu bir kere yazıp bir çok değisik veri tipleriyle birlikte hiç bir performans
kaybı olmadan kullanmayı vaad eder. CLR de yazılan genericlerin benzerlerine göre daha
sade koda, bu sayede daha kolay okunabilir ve bakımı yapılabilir olmaları büyük bir
avantajdır.
C# ile kodun tekrar kullanılması yönünde bir çok kolaylıkların gelmesine ek olarak tekrar
tekrar yapmamız gereken bazı karmasık kod parçaları için de yeni yeni çözümler
üretilmistir. Mesela enum sabitleri için yenileciler(iterators). Yenileyiciler sayesinde enum
sabitleri ile çalısmak daha rahat bir hal almıstır. Bilgisayar bilimlerinde arastırmalarda
kullanılan CLU, Sather ve Icon programlama dillerindeki özelliklerden esinlenerek foreach
blokları içinde hangi veri tiplerinin nasıl ne sekilde yenileyicilerin kullanılmasının
tanımlanması mümkün hale gelmistir.
Anonim metodlar (anonymous methods) da C# diline Whidbey ile girecek. Bu tür
metodlar ile yazmıs olduğumuz bir kod parçasını bir delege içine koyup daha sonra
kullanacağız. Anonim metodlar programlama dillerinin incelendiği derslerde geçen lamda
function fikri üzerine kurulmustur ve Lisp ve Phyton dillerinde uygulanmıstır. Bu tür
metodlar kullanılacakları anda ve yerde tanımlanıyorlar. Normalde bir fonksiyon daha
önce tanımlanır ve derleyici onun imzasını (method signature ) bilmek ister. Böylelikle
anonim metodlar, özellikle metodun yaptığı is veya metodun imzasının çalısma anında
değismesinin gerektiğinde bazı islemlerin yapılmasını daha uygun ve kolay hale gelir.
Son olarak Whidbey C# ile programcılar bir veri tipinin tamamını tek bir yerde değil
değisik kaynak dosyalarında tanımlayabilecekler. Bu tür tipler parçalı tip (partial types)
olarak adlandırılacaklar. Ayrıca parçalı tipler genis projelerde daha kolay program
tasarımı ve kod yazımı imkanı sağlayacaktır.
C# dilindeki öngörülen yenilikler ile hem büyük projeler için gelistirilecek platformların
tasarımcıları hem de yazılım mimarları (software artitechts) için favori dil olmaya devam
edecektir. Ayrıca modern söz dizimi ve bilesen yönelimli özellikleri (componentoreineted)
ile koda odaklanmıs RAD aracı olarak karsımıza çıkacaktır.
Visual J#
J# Whidbey ile planlamıs bir çok yenilik gelecektir. Bunların amacı programcıların sahip
oldukları Java deneyimlerini daha iyi bir biçimde .NET ortamında kullanmaları yönündedir.
Yeniliklerin basında Borwser Controls ve J# dilinin gelistirilmesini sayabiliriz.
J#’ın 2002’de .NET’e katılması ile Java programcıları önceden yazdıkları Java Appletlerini
.NET koduna çevirebilmek ve .NET ortamında da Applet türü yazılımlar gelistirebilmeyi
talep ettiler. Programcıların bu isteklerine cevap olarak Microsoft J# Browser Controls
adlandırılan teknolojiyi gelistirdi. Su anda beta asamasında olan bu teknoloji sayesinde
var olan applet kaynak kodlarını açıp tekrar J# ile (çok çok az kod değisikliği ile)
derlemek yeterli olacaktır. Bu teknolojinin tam olarak kullanılmaya baslandığı günlerde
programcılar kendi J# Browser Control’larını tıpki Java appletini bir web sayfasına gömer
gibi gömebilecekler. Ek olarak, tabiki, J# Browser Control’ları .NET Framework’unun tüm
olanaklarına erisim hakları olacak ve XML web servislerinin kullanımı mümkün olacak.
J#’a eklenecek yenilikler ile .NET dilleri arası uyumluluğu artacak ve Windows isletim
sisteminin özelliklerine erisim daha rahat olacaktır. Đlk olarak yeni J#’ta Enum sabitleri ve
değer tipleri kavramları ile J# CLS’ye daha uyumlu olacak. Đkincisi ise volatile ve assert
anahtar kelimelerinin eklenmesi ile daha esnek ve daha optimize olarak çalısan program
kodlarına sahip olacağız. Son olarak generic’lerin J# içinden çağrılabilmesi ile diğer .NET
dilleri ile daha da uyumlu olacaktır.
Java programcıları için hem alısık bir söz dizimi hem de nesne yönelimli özellikleri ile .NET
ortamında kolayca yazılım gelistirebilecekleri dil olarak J# öne çıkacaktır. Whidbey J#’ta
gün yüzüne çıkacak harika özellikler sadece Java ve J++ programcılarını değil bilgisayar
bilimlerinde eğitim gören öğrenciler ve onların hocalarını çok mutlu edecektir.
C# ile Rastgele Kod Üretimi
Bu uygulama birçok, yerde isimize yarayabilecek bir “Rastgele Kod Üretici” dir. Rastgele
üretilmis bir koda birçok yerde ihtiyaç duyabiliriz. Örneğin; web sitenizin üye kayıtlarında
üye adaylarının gerçek email adreslerini girmelerini garantilemek isteyebilirsiniz. Bunu
sağlamanın en basit yolu, kisinin verdiği email adresine rastgele ürettiğinz bir kodu
göndermektir. Böylece üye adayından, üyelik islemlerinin tamamlanarak hesabın aktive
olabilmesi için, email adresine gönderdiğiniz aktivasyon kodunu “üyelik aktivasyon”
sayfanızda girmesini isteyebilirsiniz. Eğer email adresi doğru değilse aktivasyon kodunu
edinemeyeceğinde üyeliği de geçerli olmaz.
Rastgele kod üretebilmek için kullanacağımız en önemli sınıf “System” isim alanı
(namespace) içerisinde bulunan “Random” sınıfıdır (class). Bu sınıfı kullanarak kod
içerisinde görünmesini istediğimiz karakterler dizisinin boyutu kadar rastgele tamsayı
üreteceğiz.
Kullanacağımız diğer bir sınıf ise System.Text isim alanı içerisinde bulunan StringBuilder
sınıfıdır. Yapacağımız islem bir metin birlestirme döngüsü içermekte ve metin birlestirme
islemlerinde StringBuilder sınıfı, string tipine oranla daha fazla performans sağlamaktadır.
Uygulamayı bir fonksiyon olarak hazırlayacağız.
Fonksiyon üreteceği “rastgele kod” un kaç karakter uzunlukta olması istendiğini
“codeLength” parametresiyle alacak. Ürettiği “codeLength” kadar karakter uzunluğundaki
“Rastgele Kod”u da string veri tipinde, fonksiyonun çağırıldığı yere döndürecek.
private string GenerateCode(int codeLength)
{
}
Fonksiyonda ilk olarak “sb” değisken adıyla, “rastgele kod”u yapılandıracağımız
StringBuilder nesnesini ve ikinci olarak da “objRandom” adıyla, rastgele sayı üretecek
olan Random nesnesini yapılandıracağız.
System.Text.StringBuilder sb=new System.Text.StringBuilder();
System.Random objRandom=new System.Random();
Sıra “Rastgele Kod”umuz içinde yer almasını istediğimiz karakterleri bir metin dizisi
olarak tanılamaya geldi. Ben bu örnekte “A-Z”, “a-z” ve “0-9” arası karakterleri
kullandım. Siz isterseniz uygulamayı zenginlestirmek için farklı karakterler de
kullanabilirsiniz.
string[] strChars = { “A", "B", "C", ...
"1", "2", "3", ...
“a", "b", "c", ... };
Simdi islemlere baslayabiliriz. Önce rastgele üreteceğimiz sayı aralığını bulalım.
Yukarıdaki karakterler “strChars” adında bir metin dizisinde tutulmaktalar. Diziler 0
indeksle basladıklarından rastgele üretilecek olan minimum rakam 0 olmalıdır. Üretilecek
maksimum rakam ise dizinin en son elemanının indeksi olmalıdır. Dizinin en büyük
indeksli elemanının indeks bilgisini
int maxRand=strChars.GetUpperBound(0);
koduyla aynı anda hem bu değeri tutacak olan “maxRand” adında bir değisken
tanımlayarak dizinin “GetUpperBound(0)” metoduyla alırız.
“Rastgele Kod”un üretilmesi, istenilen kod uzunluğu için her bir basamağın rastgele
olusturulmasıyla sağlanır. Bunun için, 0 ile “Rastgele Kod” için kullanılacak karakter
dizisinin en büyük indeksi arasında rastgele bir sayı objRandom.Next(maxRand)
metoduyla üretilir ve bu değer “rndNumber” değiskenine atanır.
int rndNumber=objRandom.Next(maxRand);
Karakter dizisindeki rastgele bir eleman, edinilen “rndNumber” sayısını indeks olarak
kullanarak “strChars(rndNumber)” ifadesiyle elde edilir ve bu karakter sb.Append
metoduyla “sb” nesnesine eklenir.
sb.Append(strChars[rndNumber]);
Eğer 10 karakter uzunluğunda bir “rastgele kod” istenirse, önce birinci basamak için
rastgele bir karakter üretilir, daha sonra ikinci basamak için ve bu böylece 10’a kadar
devam eder. Bu üretilen karakterler “sb” değiskeni içerisinde ard arda eklenir.
for(int i=0;i<codeLength;i++)
{
int rndNumber=objRandom.Next(maxRand);
sb.Append(strChars[rndNumber]);
}
En son olarak da StringBuilder nesnesinin içerinde yapılandırılmıs olan ve rastgele
karakterlerden meydana gelen sonuç, string veri tipine sb.ToString() metoduyla
dönüstürülerek fonksiyon sonlandırılır ve değer fonksiyonun çağırıldığı yere döndürülür.
return sb.ToString();
Kodun tamamlanmıs sekli asağıdaki gibidir.
private string GenerateCode(int codeLength)
{
System.Text.StringBuilder sb=new System.Text.StringBuilder();
System.Random objRandom=new System.Random();
string[] strChars = { "A","B","C","D","E","F","G","H","I",
"J","K","L","M","N","O","P","Q","R",
"S","T","U","V","W","X","Y","Z",
"1","2","3","4","5","6","7","8","9","0",
"a","b","c","d","e","f","g","h","i","j","k",
"l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};
int maxRand=strChars.GetUpperBound(0);
for(int i=0;i<codeLength;i++)
{
int rndNumber=objRandom.Next(maxRand);
sb.Append(strChars[rndNumber]);
}
return sb.ToString();
}
Metin(String) Đslemlerinde Performansı Artırmak
Programlama yaparken sık sık metinsel islemler yapmak durumunda kalırız.
Bir metni baska bir metinle birlestiririz...
Büyükçe bir metnin içerisinden bir bölümünü alırız...
Đki metni birbiriyle karsılastırırız...
....
Bu liste böylece uzar gider. Metinsel islemler denilince akla gelen ilk sey metin
birlestirme islemidir. “Bir metnin bir diğerine eklenmesi.” Đs metinle ilgili olduğunda bu
isi gerçeklestirmek için C# ta ilk akla gelen veri tipi “string” dir. “System.String”
sınıfını (class) temsil eden string bir referans türüdür. Ancak biraz farklı bir referans
türüdür. Yapı olarak “immutable”, yani değismezdir/sabittir ve bu yüzden bir kez
yapılandırıldıktan sonra içeriği değistirilemez. Đste bu yapıdan dolayıdır ki bir string
değiskene ancak bir kez değer atayabilirsiniz.
Peki ya asağıdaki kod?
string metin="Merhaba";
metin=metin + " Dünya!"; // metin=”Merhaba dünya!”
String bir referans türü olduğu için bellekteki öbek (heap) bölgesinde yaratılır ve
içeriğine “Merhaba” yüklenir. Öbekteki bu nesneye kodun erisebilmesi için gerekli adres
bilgisi ise belleğin yığın (stack) bölgesinde yaratılan “metin” değiskenine yazılır. Yani
referans türlerde değisken nesneyi değil, nesnenin adresini tutar.
Simdi ikinci satıra bakalım. Birinci satırda, metin değiskeninin temsil ettiği string
nesnesine “birinci string nesnesi” diyelim. Đkinci satırdaki kod islendiğinde “birinci string
nesnesi” üzerine yeni değer ekelenemeyeceğinden, öbekte ikinci bir string nesnesi
türetiliyor ve islem sonucu, yani “Merhaba Dünya!” bu ikinci string nesnesine
yükleniyor.
Ve kilit nokta!
“metin” değiskeni ikinci satırdaki kodun sonunda artık “birinci string nesnesi”nin değil,
“ikinci string nesnesi”nin adresini tasımaya baslıyor. Yani artık “birinci string nesnesi”
tamamen erisilemez oluyor ve “Çöp Toplayıcı” (Garbage Collector) devreye girip belleği
temizleyene kadar bellekte yer tutmaya devam ediyor.
Đste “string” veri tipinin bu yapısı nedeniyle metinsel islemlerde daha iyi performans
sağlamak amacıyla System.Text isim alanı içine bulunan “StringBuilder” sınıfı
kullanılmaktadır.
Algoritmaya bağlı olarak değismekle birlikte, StringBuilder sınıfının çok çok daha hızlı
olduğu söylenebilir.
'string' veri tipi kullanilarak metin birlestirme.....
Baslangiç : 24.11.2003 20:31:06
Bitis : 24.11.2003 20:31:08
Toplam süre : 2193,1536 milisaniye
'StringBuilder' sinifi kullanilarak metin birlestirme.....
Baslangiç : 24.11.2003 20:31:08
Bitis : 24.11.2003 20:31:08
Toplam süre : 20,0288 milisaniye
Uygulamaya sonlandirmak içim [Enter] tusuna basin.
Asağıdaki örnek kod ve yukarıdaki ekran görüntüsü, metin birlestirme islemlerinde
gerçekten çok iyi performans gösteren StringBuilder sınıfının, string ile karsılastırıldığı
bir Console uygulamasıdır.
static void Main(string[] args)
{
// Degisken tanimlari
string str="";
System.Text.StringBuilder sb=new System.Text.StringBuilder();
DateTime start; // islemin baslangiç ani
DateTime end; // islemin bitis ani
TimeSpan sure; // islemin toplam süresi
// string veri tipiyle metin birlestirme islemi.....
Console.WriteLine("'string' veri tipi kullanilarak metin birlestirme.....");
start=DateTime.Now; // islemin baslangiç zamani
Console.WriteLine("Baslangiç :\t" + start.ToString());
// Metin birlestirme islemi.....
for(int i=0;i<10000;i++)
{
str +=i.ToString();
}
end=DateTime.Now; // islemin bitis zamani
Console.WriteLine("Bitis :\t\t" + end.ToString());
sure=end.Subtract(start); // geçen süre hesaplaniyor..
Console.WriteLine("Toplam süre :\t" + sure.TotalMilliseconds + " milisaniye");
//-------------------------------------------------------
Console.WriteLine();
// StringBuilder sinifiyla metin birlestirme islemi.....
Console.WriteLine("'StringBuilder' sinifi kullanilarak metin birlestirme.....");
start=DateTime.Now; // islemin baslangiç zamani
Console.WriteLine("Baslangiç :\t" + start.ToString());
// Metin birlestirme islemi.....
for(int i=0;i<10000;i++)
{
sb.Append(i.ToString());
}
end=DateTime.Now; // islemin bitis zamani
Console.WriteLine("Bitis :\t\t" + end.ToString());
sure=end.Subtract(start);// geçen süre hesaplaniyor..
Console.WriteLine("Toplam süre :\t" + sure.TotalMilliseconds + " milisaniye");
//-------------------------------------------------------
Console.WriteLine();
Console.WriteLine("Uygulamaya sonlandirmak içim [Enter] tusuna basin.");
Console.ReadLine();
}
"Builder" Tasarım Deseninin C# ile Gerçeklestirilmesi
Bu yazıda "Creational" desenler grubunda yer alan "Builder" tasarım deseninden ve bu
deseni C# ile nasıl gerçeklestirebileceğimizden bahsedeceğim.
Bu yazıyı okumadan önce "Singleton" ve "Abstract Factory" tasarım desenlerini konu
alan yazılarımı okumanızı tavsiye ederim.
"Builder" deseni adından da anlasılacağı üzere bir nesnenin olusturulması ile ilgilidir. Bu
desende kullanılan yapılar hemen hemen "Abstract Factory" deseninde kullanılan
yapılar ile aynıdır. Bu iki desen arasında çok küçük farklılıklar vardır. Bu farkları
ilerleyen kısımlarda açıklayacağım.
"Builder" deseni birden fazla parçadan olusan kompleks yapıdaki bir nesnenin
olusturulmasını ve bu kompleks nesnenin olusturulma safhalarını istemci modülünden
tamamen gizlemek için kullanılır. Kompleks nesnenin yaratılması istemci modülünden
tamamen yalıtıldığı için nesnenin yaratılması ile ilgili islemler farklı versiyonlarda
tamamen değistirilebilir, bu istemci programın çalısmasını hiç bir sekilde
etkilemeyecektir. Burda dikkat edilmesi gereken nokta ise sudur : bu desen kompleks
nesneyi olusturan yapıların gerçek nesneden tamamen bağımsız bir yapıda olduğu
durumlarda kullanıldığı zaman esneklik getirecektir. Dolayısıyla her bir farklı parçanın
kompleks nesnede kullanılması, kompleks nesnenin islevini değistirmeyeceği gibi
sadece görünümünü yada tipini değistirecektir.
Builder deseninin Abstract Factory deseninden farkına gelince; "Abstract Factory"
deseninde soyut fabrika olarak bilinen yapının metotları fabrikaya ait nesnelerin
yaratılmasından bizzat sorumludur. Builder deseninde ise aynı mekanizma biraz daha
farklı islemektedir. Builder deseninde istemci modülü, nesnelerin ne sekilde
olusturulacağına soyut fabrika yapısına benzer bir yapı olan Builder yapısının metotları
ile
karar verir ve istemci olusturulan bu nesneyi Builder sınıfından tekrar talep eder.
Yukarıdaki soyut açıklamalardan sonra daha gerçekçi bir örnekle bu deseni açıklamamın
sizin ruh haliniz açısından faydalı olacağını düsünüyorum. Farklı donanım ürünlerinin
biraraya getirilerek bilgisayar sistemlerinin olusturulduğu bir teknik servisi göz önünde
bulunduralım. Bir müsteri bu servise gelerek çesitli özellikleri içinde barındıran bir
bilgisayar talep eder. Đsteği alan servis temsilcisi bu isteği teknik birimlere iletir
ardından teknik eleman istenilen özelliklerde bir bilgisayarın olusması için gerekli
donanımları raflarından alır ve birlestirir. Sonuçta müsterinin isteği yerine gelir ve
karsılıklı bir mutluluk havası içine girilir. Tabi bizi ilgilendirin müsterinin ve satıcının
mutluluğu değil elbette. Bizi ilgilendiren nokta müsterinin bilgisayarı olusturuan
parçaların birlestirilmesinden tamamen soyutlandığıdır. Dikkat ederseniz verilen
özelliklerde bilgisayarın olusmasında müsterinin hiç bir etkisi olmamıstır. Eğer müsteri
son anda vazgeçip farklı özelliklerde bir bilgisayar istemis olsaydı yine bir etkisi
olmayacaktı. Bu durumda eski bilgisayarın ilgili parçaları değistirilip yeni isteğe uygun
parçalar takılacaktı. Burda da kompleks bir yapı olan bilgisayar sisteminin kendisini
olusturan parçalardan (donanım) tamamen bağımsız bir yapıda olduğunu görüyoruz.
Herhangi bir diskin yada monitörün değismesi bilgisayarı bilgisayar yapmaktan
çıkarmayacak sadece bilgisayarın tipini ve görüntüsünü değistirecektir. Bu durum bizim
yukarıda açıkladığımız builder deseninin amacına tam olarak uymaktadır. Zira birazdan
bir teknik servisteki bu isleri otomatize eden temel desenimizi C# ile nasıl
gerçeklestirebileceğimizi göreceğiz.
Builder desenin de temel olarak 4 yapı bulunmaktadır. Bu yapılar sırasıyla söyledir :
• "Builder" Arayüzü : Bu yapı içinde gerçek/somut(concrete) Builder sınıflarının
uygulaması gereken özellikleri ve metotları barındırır. "Builder" abstract bir sınıf
olabileceği gibi bir arayüz de olabilir. En çok tercih edilen yapı arayüz olmasına
rağmen uygulamanızın ihtiyacına göre abstract sınıflarıda kullanabilirsiniz.
Gerçek örneğimizde bu yapı bir bilgisayar sisteminin minimum olarak
uygulaması gereken özellikleri temsil eder. Örneğin kullanıcılara satılacak her
bilgisayar sistemi mutlaka HDD,RAM gibi yapıları içerisinde bulundurmalıdır.
Dolayısıyla birbirinden farklı bilgisayar sistemlerinin bu tür yapıları mutlak olarak
içerisinde barındırabilmesi için zorlayıcı bir etkene diğer bir değisle abstract üye
elemanlara yada arayüzlere ihtiyacımız vardır.
• "Concrete(Somut) Builder" Yapısı : Gerçek bir bilgisayar sistemini temsil
eden ana yapımızdır. Farklı farklı donanımların bir araya getirilmesi ile çok
sayıda sistem olusturulabilir. Olusturulabilecek her sistem gerçek bir Builder
yapısına denk düsmektedir.
• Product(Ürün) : Gerçek bir bilgisayar sistemini ve bu sistemde hangi
özelliklerin bulunduğunu somut olarak yapısında barındıran bir modeldir. Bu
noktada bir önceki Concrete Builder yapısının parçaları olusturduğunu, bu
parçaların birlesmesinden olusan kompleks nesneninde Product olduğunu
belirtmekte yarar var.
• Director(Yönetici) : Genellikle istemci ile yani örneğimizdeki müsteri ile
Builder(parçaları birlestirip ürün yapan birimler) yapıları arasında köprü
görevinde bulunan yapı olarak bilinir. Kısacası bilgisayar sisteminin olusması için
raflardan donanımları alıp onları birlestiren teknik eleman Director görevindedir.
Tabi Director yapısının is yaparken Builder arayüzünün kısıtlarına göre çalısması
gerektiğinide belirtmek gerekir.
UML Modeli
Asağıdaki sekil "Builder" tasarım deseninin UML sınıf diagramını göstermektedir.
Semadaki her bir sekil desendeki iligili yapıyı modellemektedir. Ayrıca desendeki sınıflar
arasındaki iliskilerde detaylı bir sekilde gösterilmistir.
Yukarıdaki UML diyagramını kısaca özetleyecek olursak : Builder arayüzünde bulunan
metotlar yardımıyla gerçek builder sınıflarında kompleks nesnenin parçaları
birlestirilerek istenilen nesne olusturulur. Kompleks sistemi olusturan parçalar farklı
sayılarda olabildği halde minumum gereklilikleri de sağlamak zorundadır. Bu minumum
gereksinimler Builder arayüzünde bildirilmistir. Director sınıfındaki bir metot, her sistem
için standart olan Builder arayüzündeki metotları kullanarak hangi sistemin
olusturulduğundan bağımsız olarak kompleks nesnenin olusturulması sağlanır. Yani
Director hangi tür bir bir bilgisayar sisteminin olusturulacağı ile ilgilenmez. Director
sınıfı sadece kendisine gönderilen parametre sayesinde Builder arayüzünün
metotlarından haberdardır. Son olarak istemci Director sınıfının ilgili metotlarını
kullanarak gerçek ürünün olusmasını sağlar. Bunun için elbette Director sınıfının ilgili
metoduna hangi ürünün olusturulacağını bildirmesi gerekir.
Bu kısa açıklamadan sonra yukarıdaki diyagramı baz alarak modern bir programlama
dili olan C# ile bu deseni nasıl gerçeklestirebileceğimizi inceleyelim.
Builder Deseninin C# ile Gerçeklestirilmesi
! Asağıdaki kodları kısaca inceleyip açıklamaları okumaya devam edin.
using System;
namespace BuilderPattern
{
public interface IBilgisayarToplayicisi
{
Bilgisayar Bilgisayar{get;}
void CDROM_Olustur();
void HDD_Olustur();
void Monitor_Olustur();
void RAM_Olustur();
}
public class GoldPC : IBilgisayarToplayicisi
{
private Bilgisayar mBilgisayar;
public Bilgisayar Bilgisayar
{
get{return mBilgisayar;}
}
public GoldPC()
{
mBilgisayar = new Bilgisayar("Gold-PC");
}
public void CDROM_Olustur()
{
mBilgisayar["cdrom"] = "52X GoldStar";
}
public void HDD_Olustur()
{
mBilgisayar["hdd"] = "60 GB Seagate";
}
public void Monitor_Olustur()
{
mBilgisayar["monitor"] = "17' Hyundai";
}
public void RAM_Olustur()
{
mBilgisayar["ram"] = "512 MB DDR Kingston";
}
}
public class SilverPC : IBilgisayarToplayicisi
{
private Bilgisayar mBilgisayar;
public Bilgisayar Bilgisayar
{
get{return mBilgisayar;}
}
public SilverPC()
{
mBilgisayar = new Bilgisayar("Silver-PC");
}
public void CDROM_Olustur()
{
mBilgisayar["cdrom"] = "48X Creative";
}
public void HDD_Olustur()
{
mBilgisayar["hdd"] = "30 GB Maxtor";
}
public void Monitor_Olustur()
{
mBilgisayar["monitor"] = "15' Vestel";
}
public void RAM_Olustur()
{
mBilgisayar["ram"] = "256 MB SD Kingston";
}
}
public class Bilgisayar
{
private string mBilgisayarTipi;
private System.Collections.Hashtable mParcalar = new
System.Collections.Hashtable();
public Bilgisayar(string BilgisayarTipi)
{
mBilgisayarTipi = BilgisayarTipi;
}
public object this[string key]
{
get
{
return mParcalar[key];
}
set
{
mParcalar[key] = value;
}
}
public void BilgisayariGoster()
{
Console.WriteLine("Bilgisayar Tipi : " + mBilgisayarTipi);
Console.WriteLine("---> CD-ROM Model : " + mParcalar["cdrom"]);
Console.WriteLine("---> Hard Disk Model : " + mParcalar["hdd"]);
Console.WriteLine("---> Monitör Model : " + mParcalar["monitor"]);
Console.WriteLine("---> RAM Model : " + mParcalar["ram"]);
}
}
public class TeknikServis
{
public void BilgisayarTopla(IBilgisayarToplayicisi bilgisayarToplayicisi)
{
bilgisayarToplayicisi.CDROM_Olustur();
bilgisayarToplayicisi.HDD_Olustur();
bilgisayarToplayicisi.Monitor_Olustur();
bilgisayarToplayicisi.RAM_Olustur();
}
}
}
Kaynak koddan da görüleceği üzere en temel yapımız IBilgisayarToplayicisi arayüzüdür.
Bu arayüzde bir Bilgisayar ürününü temsil etmek için bir özellik ve bilgisayarı olusturan
parçaları olusturmak için gereken metotlar bulunmaktadır. Bu metotların bu arayüzden
türeyen bütün sınıflar tarafından uygulanması gerekmektedir. Örneğimizde bir GoldPC
ve SilverPC bilgisayar modelleri için gereken bilesenleri olusturmak için 4 adet metot
bulunmaktadır. Burada en önemli nokta her Bilgisayar tipinde bir özelliğinde bulunması.
Bu özellik istendiği zaman olusturulan Bilgisayar ürününü istemciye vermektedir. Zaten
dikkat ederseniz her bir sisteme ilisikin özellikler Bilgisayar sınıfındaki Hashtable
nesnesinde saklanmıstır. Ayrıca her Bilgisayar nesnesinin ayrıca bir tip bilgisi
saklanmaktadır. Son olarak TeknikServis isimli sınıfımızı ele alalım. Bu sınıf Builder
desenindeki Director yapısına denk düsmektedir. Bu sınıftaki BilgisayarTopla metodu
kendisine gönderilen bilgisayartoplayıcısı arayüzü referansına ait metotları kullanarak
Bilgisayar nesnesinin parçalarını kendisi olusturmaktadır. Bu örnekte bilgisayarı
olusturan her parça kafa karıstırmamak için ayrı bir sınıf olarak tasarlanmamıstır.
Bunun yerine string türünden değerlere sahip olan bir hashtable nesnesi kullanılmıstır.
Not : Yukarıdaki örnek gerçek otomasyonda birebir kullanılamayabilir. Örneğin her bir
bilgisayar modeli için ayrı ayrı sınıf tasarlamak hos olmayabilir. Bu sorunu daha dinamik
bir sekilde çözmek gerekir. Buradaki örnek sadece Builder desenin daha rahat bir
sekilde kavrayabilmeniz için verilmistir.
Son olarak yukarıdaki yapıları kullanan bir istemci programı yazıp desenimizi test
edelim.
using System;
namespace BuilderPattern
{
class Class1
{
static void Main(string[] args)
{
TeknikServis teknikservis = new TeknikServis();
IBilgisayarToplayicisi BT1 = new GoldPC();
IBilgisayarToplayicisi BT2 = new SilverPC();
tekniksevis.BilgisayarTopla(BT1);
teknikservis.BilgisayarTopla(BT2);
BT1.Bilgisayar.BilgisayariGoster();
Console.WriteLine("-------------");
BT2.Bilgisayar.BilgisayariGoster();
}
}
}
Programı çalıstırdığımızda asağıdaki ekran görüntüsünü elde ederiz.
Bilgisayar Tipi : Gold-PC
---> CD-ROM Model : 52X GoldStar
---> Hard Disk Model : 60 GB Seagate
---> Monitör Model : 17' Hyundai
---> RAM Model : 512 MB DDR Kingston
-------------
Bilgisayar Tipi : Silver-PC
---> CD-ROM Model : 48X Creative
---> Hard Disk Model : 30 GB Maxtor
---> Monitör Model : 15' Vestel
---> RAM Model : 256 MB SD Kingston
Desenlerle ilgili bir sonraki yazımda Creational desenlerden olan "Prototype" desenini
ele alacağım. Herkese iyi çalısmalar.
Kod Optimizasyonu ve "volatile" Anahtar Sözcüğü
Bu yazıda C#'ın önemli ama tam olarak neden kullanıldığı bazı profesyonel programcılar
tarafından bile pek fazla bilinmeyen bir anahtar sözcük olan volatile üzerinde duracağız.
Bir çok popüler derleyici sizin isteğiniz dısında kodunuzun isleyis mantığına müdahale
edebilmektedir. Bu müdahalenin en bilinen sebeplerinden birisi uygulumanızın kod
boyutunu küçültmek yada uygulamanızın çalısma zamanını düsürmektir. Aslında biz bu
islemlerin tamamına birden optimizasyon da diyebiliriz. Zira hemen hemen bütün
derleyicilerin optimizasyon isleminin yapılıp yapılmayacağını belirten bir parametresi
vardır. C# derleyicisi için bu parametre /optimize yada kısaca /o seklindedir.
Peki optimizastondan neyi anlamalıyız? Genel olarak iki farklı sekilde optimizasyondan
bahsetmek mümkündür. Birincisi daha henüz derleme asamasındayken programcının
gözünden kaçan bazı gereksiz bildirimlerin veya tanımlamaların derleyici tarafından
derlenecek koda dahil edilmemesi ile olabilir. Örneğin hiç kullanmadığınız bir değisken
için bellekte bir alan tahsis edilmesinin hiç bir gereği yoktur. Bu yüzden hiç bir yerde
kullanılmayan değiskenlerin derleyiciniz tarafından derleme modülüne iletilmemesi bir
optimizasyon olarak görülmektedir. Bu tip optimizasyon kapsamı içinde ele alınabilecek
diğer bir örnek ise asağıdaki kod parçası ile gösterilmistir.
Not : Asağıdaki kodun bilinçsiz yada dalgın bir programcı tarafından yazıldğını
varsayıyoruz.
int a = 0;
while(a != 0)
{
a = 2 ;
a = 0 ;
}
Yukarıdaki kodda akıs hiç bir zaman while bloğunun içine gelmeyecektir. Ve üstelik eğer a
değiskeni farklı bir is parçacığı(thread) tarafından while bloğuna girmeden değistirilip akıs
while bloğuna girse bile a değiskeni while bloğu içinde tekrar eski sabit değerine atanıyor.
Dolayısıyla while bloğunda bulunan kodların çalıstırılabilir uygulama dosyasının boyutunu
büyütmekten baska bir ise yaramayacağı açıktır. O halde burda insan üstü bir
mekanizmanın devreye girip kodu optimize etmesi gerekir. Bu mekanizma elbetteki
derleyicinin optimizasyon isine yarayan parametresidir. Optimizasyon isleminde
derleyiciden derleyiciye fark olmasına rağmen yukarıdaki kod parçasının geebileceği en
optimum biçim asağıda gösterildiği gibidir.
int a = 0;
while(a != 0)
{
}
Diğer bir optimizasyon biçimi ise derleyicinin değiskenlerin elde edilmesinde yada tekrar
yazılmasında belleğin yada islemcinin tercih edilmesi ile ilgilidir. Bu noktada
mikroislemciler ile ilgili kısa bir bilgi vermekte fayda var : Kaynak kodumuz çalıstırılabilir
durumdayken aslında makine koduna çevrilmistir. Bu komutlar daha önceden
mikroislemcilerde elektronik düzeyde programlandıkları için bu komutlar tek tek
mikroislemcide kolaylıkla icra edilir. Mikroislemciler aynı anda tek bir is yapabileceği için
yapacağı islemler içinde kullandığı değiskenleri her defasında bellekten almak yerine daha
hızlı olması açısından mikroislemcideki data register dediğimiz kayıtçılarda tutar. Bu
register dediğimiz bölümlerin sınırlı sayıda bulunduğunu belirtmek gerekir. Dolayısıyla
bellekte bulunan uygulamamızın ihtiyacına göre daha doğrusu icra edilecek bir sonraki
makine kodunun çesidine göre islemci bellekten ilgili değiskenleri register'larına yükler ve
ilgili komutunu çalıstırır. Doğaldır ki bir değerin mikroislemci tarafından belleğe(ram)
yazılması ile mikroislemcideki register bölgesine yazılması arasında dağlar kadar fark
vardır. Bu fark elbette hız faktörüdür. Đste tam bu noktada ikinci tip optimizasyon kuralını
tanımlayabiliriz. Derleyici öyle bloklara rastlayabilir ki, bu bloklar içinde bulunan bir
değiskenin değerini her defasında bellekten okuyacağına bu değiskenin değerini bir
defaya mahsus olmak üzere mikroislemcinin ilgili register bölgesine bölgesine kaydeder
ve sonraki okumalarda islemci bellek yerine bu register bölgesini kullanır. Böylece
kodunuzun çalısma süresinde önemli sayılabilecek bir azalma görülür. Elbetteki bu
optimizasyon isleminin yüzlerce kez tekrarlandığını varsayarak bu sonuca varıyoruz.
Yukarıda değisken okuma ile ilgili söylediklerimin aynısı bir değiskenin değerini
değistirmek için de geçerli olduğunu belirtmeliyim. Yani siz programınızın 10. satırında bir
değiskenin değerini bir değerden baska bir değer çektiğiniz halde derleyici bu islemi
15.satırda yapabilir. Bu durumda 15. satıra kadar o değiskenin kullanılmadığı yorumunu
yapabiliriz. Bu sekilde mikroislemcinin belleğe yazma islemi geciktirilerek belli ölçüde
optimizasyon sağlanır. Tabi bu optimizasyonun ölçüsü tamamen mikroislemcinin o anki
durumuna bağlıdır.
Buraya kadar hersey normal. Bir de madolyonun öteki yüzüne bakalım. Bildiğiniz üzere
uygulamalar genellikle çoklu is parçacıklarından(multi thread) ve proseslerden olusur.
Her bir proses diğer bir proses teki değiskene isletim sisteminin izin verdiği ölçüde erisip
üzerinde islemler yapabilir. Aynı sekilde bir is parçacığıda diğer bir is parçacığında
bulunan değiskene erisip üzerinde çesitli islemler yapabilir. Peki bunun bizim
optimizasyon kurllarımızla bağlantısı ne? Söyle ki : derleyici bir değiskenin değerinin
farklı bir is parçacağı tarafından yada farklı bir proses tarafından isleneceği üzerinde
durmaz. Bu tamamen isletim sisteminin yönetimindedir. Hal böyleyken bizim yukarıda
bahsettiğimiz ikinci optimizasyon tipi bazı durumlarda yarar getireceğiniz zarar getirebilir.
Zira optimizasyon adına bir değiskenin değerini her defasında bellekten okuma yerine
mikroislemcideki ilgili register dan okurken o anda farklı bir is parçacağı yada farklı bir
proses hatta ve hatta isletim sistemi sizin erismeye çalıstığınız değiskenin değerini sizin
uygulamanızın mantığına göre değistirebilir. Bu durumda siz o değiskenin son halini
kullanmamıs olursunuz. Dolayısıyla programınızda farklı thread lar yada prosesler
arasında paylasılan veya isletim sistemi tarafından değistirilmesi muhtemel olan
değiskenlerinizi optimizasyon kuralına tabi tutmamanız gerekir. Peki bunu nasıl
basaracağız?
volatile anahtar sözcüğü burada imdadımıza yetisiyor. Bir değiskeni volatile anahtar
sözcüğü ile bildirdiğiniz takdirde derleyicinizin optimizasyon ile ilgili parametresini açık
tutsanız bile ilgili değisken yukarıda bahsi geçen tehlikeli optimizasyon kurallarına tabi
tutulmayacaktır. Yani volatile ile bildirilen değiskenlere programın akısı sırasında her
ihtiyaç duyulduğunda değiskenin gerçek yeri olan belleğe basvurulur. Aynı sekilde bir
değiskene yeni bir değer yazılacağı zaman bu yazma islemi hiç geciktirilmeden bellekteki
yerine yazılır. Böylece volatile ile bildirilen değiskenler farklı is parçacıkları yada prosesler
tarafından ortak kullanılıyor olsada programın akısı içerisinde her zaman son versiyonu
elde edilecektir. Çünkü bu değiskenlerin değeri her defasında bellekten çekilir. Her ne
kadar optimizasyondan taviz verme zorunda kalsak ta böylece uygulamalarımızda
çıkabilecek olası bugların(böcek) önüne geçmis oluruz.
volatile, C# dilindeki anahtar sözcüklerden biridir. Üye değisken bildirimi ile birlikte
kullanılır. volatile anahtar sözcüğü yalnızca asağıdaki değisken tipleri ile birlikte
kullanılabilir.
• Herhangi bir referans tipindeki değisken ile
• byte, sbyte, short, ushort, int, uint, char, float yada bool. türünden olan
değiskenler ile
• byte, sbyte, short, ushort, int, yada uint türünden semboller içeren
numaralandırmalar(enums) ile
• unsafe modda iken herhangi bir gösterici türü ile
volatile anahtar sözcüğünün kullanımına bazı örnekler verelim :
public static volatile int a;
public volatile bool a;
public volatile int* a;
....
Microsoft'un resmi dökümanlarında(MSDN) verilen bir örneği buraya tasıyarak ne gibi
durumlarda volatile anahtar sözcüğüne ihtiyaç duyacabileceğimizi görelim.
using System;
using System.Threading;
class Test
{
public static int result;
public static volatile bool finished;
static void Thread2()
{
result = 143;
finished = true;
}
static void Main()
{
finished = false;
new Thread(new ThreadStart(Thread2)).Start();
for (;;)
{
if (finished)
{
Console.WriteLine("result = {0}", result);
return;
}
}
}
}
Yukarıdaki örnek programdaki püf nokta finished isimli değiskenin ana thread ve ana
thread içinde baslatılan yeni thread tarafından ortak kullanılan bir değisken olmasıdır.
Eğer finished değiskeni volatile olarak bildirilmemis olsaydı, akıs thread2 metoduna
gelmis olmasına rağmen Main metodu içindeki if bloğu çalıstırılmayabilirdi. Çünkü
derleyici ana thread içinden finished değiskeninine tampolanmıs bir bölgeden(register)
erisebilir. Bu durumda finished değiskeninin gerçek değeri true olmasına rağmen ana
thread de finished değiskeni halen false olarak ele alınır. Bu yüzden finished değiskeninin
her durumda son versiyonunu elde etmek için bu değisken volatile anahtar sözcüğü ile
bildirilmistir.
volatile anahtar sözcüğünün kullanımına bir örnek daha verelim. Belli bir anda bir sınıftan
sadece bir nesnenin olusmasını sağlayan Singleton desenini daha önceki bir makalemde
ele almıstım. Bu konu ile ilgili bilgi eksikliğiniz varsa ilgili makaleyi okumanızı tavsiye
ederim. Bahsi geçen makalede verilen desenin bir uygulaması asağıda ki gibi yeniden
yazılmıstır.
public class SingletonDeseni
{
private static volatile SingletonDeseni nesne;
private static Object kanalKontrol = new Object;
private SingletonDeseni()
{
}
public static Singleton Nesne()
{
if(nesne == null)
{
lock(kanalKontrol)
{
if(nesne == null)
{
nesne = new SingletonDeseni();
}
}
}
return nesne;
}
}
Bu örnekte SingletonDeseni nesnesinin belli bir anda tekil olarak bulunduğunu çok kanallı
uygulamalar içinde geçerli kılmak için bu nesne volatile olarak bildirilmistir. Üstelik bu
örnekte farklı bir prosesin müdahalesi olsa bile bu nesneden ancak ve ancak bir adet
yaratılacaktır.
Son olarak volatile kelimesinin sözlük anlamı üzerinde durmak istiyorum. Đki yıl önce
Đngilizce'den Türkçe'ye çevrilmis bir Visual C++ kitabını okuduğumda volatile ile
bildirilmis değiskenlerden oynak(!) değiskenler diye bahsedildiğine sahit oldum. Đlk basta
bu ilginç kullanım bana birsey ifade etmedi ama hislerimin yardımıyla aslında yazarın
volatile dan bahsettiğine karar verdim. Sizde takdir edersiniz ki yukarıda anlattıklarımız
ile "oynak" kelimesi arasında pek bir bağ bulunmamaktadır. Kitabın çevirisini yapan yazar
muhtemelen bir programcı değil bir çevirmendi. Çünkü eğer iyi bir programcı olsaydı
oynak kelimesi yerine daha uygun bir kelime seçilebilirdi. volatile'ın sözlük anlamı "uçucu
olan", "buhar olan" anlamına gelmektedir. Ancak ben henüz volatile sözcüğüne kendi
mesleğimizle ilgili uygun bir karsılık bulamadım. Bu konuda daha önce cdili ve cdernek
isimli iki yahoo grubunda çesitli tartısmalar olmustur. Bu gruplara üye olarak ilgili
tartısmalarda geçen konusmaları okumanızı öneririm. Eğer sizin de bu konuda önerileriniz
varsa bizimle paylasırsanız seviniriz.
Umarım faydalı bir yazı olmustur. Herkese iyi çalısmalar...
Overloaded(Asırı Yüklenmis) Metotların Gücü
Bu makalemde sizlere overload kavramından bahsetmek istiyorum. Konunun daha iyi
anlasılabilmesi açısından, ilerliyen kısımlarda basit bir örnek üzerinde de çalısacağız.
Öncelikle Overload ne demek bundan bahsedelim. Overload kelime anlamı olarak Asırı
Yükleme anlamına gelmektedir. C# programlama dilinde overload dendiğinde, aynı isme
sahip birden fazla metod akla gelir. Bu metodlar aynı isimde olmalarına rağmen, farklı
imzalara sahiptirler. Bu metodların imzalarını belirleyen unsurlar, parametre sayıları ve
parametre tipleridir. Overload edilmis metodları kullandığımız sınıflarda, bu sınıflara ait
nesne örnekleri için aynı isme sahip fakat farklı görevleri yerine getirebilen ( veya aynı
görevi farklı sayı veya tipte parametre ile yerine getirebilen ) fonksiyonellikler kazanmıs
oluruz.
Örneğin;
Sekil 1 : Overload metodlar.
Sekil 1 de MetodA isminde 3 adet metod tanımı görüyoruz. Bu metodlar aynı isime sahip
olmasına rağmen imzaları nedeni ile birbirlerinden tamamıyla farklı metodlar olarak
algılanırlar. Bize sağladığı avantaj ise, bu metodları barındıran bir sınıf nesnesi
yarattığımızda aynı isme sahip metodları farklı parametreler ile çağırabilmemizdir. Bu bir
anlamda her metoda farklı isim vermek gibi bir karısıklığında bir nebze önüne geçer. Peki
imza dediğimiz olay nedir? Bir metodun imzası su unsurlardan olusur.
Metod Đmzasın Kabul Edilen
Unsurlar
Metod Đmzası Kabul
Edilmeyen Unsurlar
• Parametre sayısı
• Parametrelerin tipleri
• Metodun geri dönüs
tipi
Tablo 1. Kullanım Kuralları
Yukarıdaki unsurlara dikkat ettiğimiz sürece dilediğimiz sayıda asırı yüklenmis (overload
edilmis) metod yazabiliriz.
Simdi dilerseniz küçük bir Console uygulaması ile , overload metod olusumuna engel
teskil eden duruma bir göz atalım. Öncelikle metodun geri dönüs tipinin metodun imzası
olarak kabul edilemiyeceğininden bahsediyoruz. Asağıdaki örneğimizi inceleyelim.
using System;
namespace Overloading1
{
class Class1
{
public int Islem(int a)
{
return a*a;
}
public string Islem(int a)
{
string b=System.Convert.ToString(a);
return “Yasim:”+b;
}
[STAThread]
static void Main(string[] args)
{
}
}
}
Örneğin yukarıdaki uygulamada, Islem isimli iki metod tanımlanmıstır. Aynı parametre
tipi ve sayısına sahip olan bu metodların geri dönüs değerlerinin farklı olması neden ile
derleyici tarafından farklı metodlar olarak algılanmıs olması gerektiği düsünülebilir. Ancak
böyle olmamaktadır. Uygulamayı derleme çalıstığımızda asağıdaki hata mesajı ile
karsılasırız:
Overloading1.Class1' already defines a member called 'Islem'
with the same parameter types
Yapıcı metodları da overload edebiliriz. Bu nokta oldukça önemlidir; ki metodları overload
etmeyi .NET ile yazılım gelistirirken sıkça kullanırız. Örneğin, SqlConnection sınıfından bir
nesne örneği olusturmak istediğimizde bunu yapabileceğimiz 2 tane overload edilmis
yapıcı metod olduğunu görürüz. Bunlardan birisi asağıda görülmektedir.
Sekil 2. Örnek bir Overload Constructor(Asırı Yüklenmis Yapıcı) metod.
Dolayısıyla bizde yazdığımız sınıflara ait constructorları overload edebiliriz. Simdi
dilerseniz overload ile ilgili olaraktan kısa bir uygulama gelistirelim. Bu uygulamada
yazdığımız bir sınıfa ait constructor metodları overload ederek değisik tipte
fonksiyonellikler edinmeye çalısacağız.
Bu uygulamada KolayVeri isminde bir sınıfımız olucak. Bu sınıfın üç adet yapıcısı olucak.
Yani iki adet overload constructor yazıcaz. Đki tane diyorum çünkü C# zaten default
constructoru biz yazmasak bile uygulamaya ekliyor. Bu default constructorlar parametre
almayan constructorlardır. Overload ettiğimiz constructor metodlardan birisi ile,
seçtiğimiz bir veritabanına bağlanıyoruz. Diğer overload metod ise, parametre olarak
veritabanı adından baska, veritabanına bağlanmak için kullanıcı adı ve parola
parametrelerinide alıyor. Nitekim çoğu zaman veritabanlarımızda yer alan bazı tablolara
erisim yetkisi sınırlamaları ile karsılasabiliriz. Bu durumda bu tablolara bağlantı açabilmek
için yetkili kullanıcı adı ve parolayı kullanmamız gerekir. Böyle bir olayı canlandırmaya
çalıstım. Elbetteki asıl amacımız overload constructor metodların nasıl yazıldığını, nasıl
kullanıldığını göstermek. Örnek gelismeye çok, hemde çok açık. Simdi uygulamamızın bu
ilk kısmına bir gözatalım. Asğıdakine benzer bir form tasarım yapalım.
Simdi sıra geldi kodlarımızı yazmaya. Öncelikle uygulamamıza KolayVeri adında bir class
ekliyoruz. Bu class’ın kodları asağıdaki gibidir. Aslında uygulamaya bu asamada
baktığımızda SqlConnection nesnemizin bir bağlantı olusturmasını özellestirmis gibi
oluyoruz. Gerçekten de aynı islemleri zaten SqlConnection nesnesini overload
constructor’lari ile yapabiliyoruz. Ancak temel amacımız asiri yüklemeyi anlamak olduğu
için programın çalısma amacının çok önemli olmadığı düsüncesindeyim. Umuyorum ki
sizlere asırı yükleme hakkında bilgi verebiliyor ve vizyonunuzu gelistirebiliyorumdur.
using System;
using System.Data.SqlClient;
namespace Overloading
{
public class KolayVeri
{
/* Connection'in durumunu tutacak ve sadece bu class içinde
geçerli olan bir string değisken tanımladık. private anahtar kelimesi
değiskenin sadece bu class içerisinde yasayabilceğini belirtir.
Yazmayabiliriz de, nitekim C# default olarak değiskenleri private kabul
eder.*/
private string baglantiDurumu;
/* Yukarıda belirttiğimiz baglantiDurumu isimli değiskenin sahip olduğu
değeri, bu class'a ait nesne örneklerini kullandiğımız yerde görebilmek
için sadece okunabilir olan (readonly), bu sebeplede sadece Get bloğuna
sahip olan bir özellik tanımlıyoruz.*/
public string BaglantiDurumu
{
get
{
/* Bu özelliğe eristiğimizde baglantiDurumu değiskeninin
o anki değeri geri döndürülecek. Yani özelliğin
çagırıldığı yere döndürülücek.*/
return baglantiDurumu;
}
}
/* Iste C# derleyicisinin otomatik olarak eklediği parametresiz yapıcı
metod. Biz bu yapıcıya tek satırlık bir kod ekliyoruz. Eğer nesne örneği
parametresiz bir Constructor ile yapılırsa bu durumda bağlantının kapalı
olduğunu belirtmek için baglantiDurumu değiskenine bir değer atıyoruz. Bu
durumda uygulamamızda bu nesne örneğinin BaglantiDurumu özelliğine
eristiğimizde BAĞLANAMADIK değerini elde edeceğiz.*/
public KolayVeri()
{
baglantiDurumu="BAĞLANAMADIK";
}
/* Bizim yazdığımızı asırı yüklenmis ilk yapıcı metoda gelince. Burada
yapıcımız, parametre olarak bir string alıyor. Bu string veritabanının
adını barındırıcak ve SqlConnection nesnemiz için gerekli baglantı
stringine bu veritabanının adını geçiricek.*/
public KolayVeri(string veritabaniAdi)
{
string connectionString="initial
catalog="+veritabaniAdi+";data source=localhost;integrated
security=sspi";
/* SqlConnection baglantımız yaratılıyor.*/
SqlConnection con=new SqlConnection(connectionString);
/* Baglantı islemini bir try bloğunda yapıyoruz ki, herhangi bir nedenle
Sql sunucusuna bağlantı sağlanamazsa (örnegin hatalı veritabanı adı
nedeni ile) catch bloğunda baglantiDurumu değiskenine BAĞLANAMADIK
değerini atıyoruz. Bu durumda program içinde KolayVeri sınıfından örnek
nesnenin BaglantiDurumu özelliğinin değerine baktığımızda BAĞLANAMADIK
değerini alıyoruz böylece bağlantının saglanamadığına kanaat getiriyoruz.
Kanaat dedikte aklima Üsküdarda ki Kanaat lokantasi geldi :) Yemekleri
çok güzeldir. Sanirim karnımız acıktı... Neyse kaldığımız yerden devam
edelim.*/
try
{
con.Open(); // Bağlantımız açılıyor.
/* BaglantiDurumu özelliğimiz (Property), baglantiDurumu
değiskeni sayesinde BAĞLANDIK değerini alıyor.*/
baglantiDurumu="BAGLANDIK";
}
/* Eğer bir hata olursa baglantiDurumu değiskenine
BAĞLANAMADIK değerini atıyoruz.*/
catch(Exception hata)
{
baglantiDurumu="BAGLANAMADIK";
}
}
/* Sıra geldi ikinci overload constructor metoda. Bu metod ekstradan iki
parametre daha alıyor. Bir tanesi user id'ye tekabül edicek olan
kullaniciAdi, diğeri ise bu kullanıcı için password'e tekabül edecek olan
parola. Bunları SqlConnection'in connection stringine alarak ,
veritabanına belirtilen kullanıcı ile giris yapmıs oluyoruz. Kodların
isleyisi bir önceki metodumuz ile aynı.*/
public KolayVeri(string veritabaniAdi,string kullaniciAdi,string
parola)
{
string connectionString="initial
catalog="+veritabaniAdi+";data source=localhost;user
id="+kullaniciAdi+";password="+parola;
SqlConnection con=new SqlConnection(connectionString);
try
{
con.Open();
baglantiDurumu="BAGLANDIK";
}
catch(Exception hata)
{
baglantiDurumu="BAGLANAMADIK";
}
}
}
}
Simdi sıra geldi, formumuz üzerindeki kodları yazmaya.
string veritabaniAdi;
private void lstDatabase_SelectedIndexChanged(object sender,
System.EventArgs e)
{
veritabaniAdi=lstDatabase.SelectedItem.ToString();
/* Burada kv adında bir KolayVeri sınıfından nesne örneği (object
instance) yaratılıyor. Dikkat edicek olursanız burada yazdığımı
ikinci overload constructor'u kullandık.*/
KolayVeri kv=new KolayVeri(veritabaniAdi);
/* Burada KolayVeri( dediğimizde .NET bize kullanabileceğimiz asırı
yüklenmis constructorları asagıdaki sekilde olduğu gibi
hatırlatacaktır. IntelliSence’in gözünü seveyim.*/
Sekil 4. 2nci yapıcı
stbDurumBilgisi.Text=lstDatabase.SelectedItem.ToString()+"
"+kv.BaglantiDurumu;
}
private void btnOzelBaglan_Click(object sender, System.EventArgs e)
{
string kullanici,sifre;
kullanici=txtKullaniciAdi.Text;
sifre=txtParola.Text;
veritabaniAdi=lstDatabase.SelectedItem.ToString();
KolayVeri kvOzel=new KolayVeri(veritabaniAdi,kullanici,sifre);
/* Burada ise diğer asırı yüklenmis yapıcımızı kullanarak bir
KolayVeri nesne örneği olusturuyoruz.*/
Sekil 5. 3ncü yapıcı
stbDurumBilgisi.Text=lstDatabase.SelectedItem.ToString()+"
"+kvOzel.BaglantiDurumu+" User:"+kullanici;
}
Evet simdide programın nasıl çalistığına bir bakalım. Listbox nesnesi üzerinde bir
veritabanı adına bastığımızda bu veritabanına bir bağlantı açılır.
Sekil 6. Listboxta tıklanan veritabanına bağlandıktan sonra.
Ve birde kullanıcı adı ile parola verilerek nasıl bağlanacağımızı görelim.
Sekil 7. Kullanıcı adı ve parola ile bağlantı
Peki ya yanlıs kullanıcı adı veya parola girersek?
Sekil 8. Yanlıs kullanıcı adı veya parolası sonrası.
Evet değerli okuyucular bu seferlikte bu kadar. Bir sonraki makalemizde görüsmek
dileğiyle hepinize mutlu günler, yarınlar dilerim.
C#'ta Numaralandırıcılar(Enumerators)
Bu makalemizde, kendi değer türlerimizi olusturmanın yollarından birisi olan
Enumerator'ları inceleyeceğiz. C# dilinde veri depolamak için kullanabileceğimiz temel
veri türleri yanında kendi tanımlayabileceğimiz türlerde vardır. Bunlar Structs(Yapılar),
Arrays(Diziler) ve Enumerators(Numaralandırıcılar)'dır.
Numaralandırıcılar, sınırlı sayıda değer içeren değiskenler yaratmamıza olanak sağlarlar.
Burada bahsi geçen desisken değerleri bir grup olustururlar ve sembolik bir adla temsil
edilirler. Numaralandırıcıları kullanma nedenlerimizden birisi verilere anlamlar
yüklekleyerek, program içerisinde kolay okunabilmelerini ve anlasılabilmelerini
sağlamaktır.
Örneklerimizde bu konuyu çok daha iyi anlıyacaksınız. Bir Numaralandırıcı tanımlamak
için asağıdaki sözdizimi (syntax) kullanılır.
<Kapsam belirteçleri> enum <numaralandırıcıAdi>
{
<birinciUye>,
<ikinciUye>,
<ucuncuUye>,
}
Kapsam belirteçleri protected, public, private, internal yada new değerini alır ve
numaralandırıcının geçerli olduğu kapsamı belirtir. Dikkat edilecek olursa, elemanlara
herhangi bir değer ataması yapılmamıstır. Nitekim bu numaralandırıcıların özelliğidir. Đlk
eleman 0 değerine sahip olmak üzere diğer elemanlar 1 ve 2 değerlerini sahip olucak
sekilde belirlenirler. Dolayısıyla programın herhangi bir yerinde bu numaralandırıcıya ait
elemana ulastığımızda, bu elemanın index değerine erismis oluruz. Gördüğünüz gibi
numaralandırıcı kullanmak okunurluğu arttırmaktadır.
Dilersek numaralandırıcı elemanlarının 0 indexinden değil de her hangibir değere
bağlamasını sağlayabilir ve hatta diğer elemanlarada farklı index değerleri atayabiliriz.
Basit bir numaralandırıcı örneği ile konuyu daha iyi anlamaya çalısalım.
using System;
namespace enumSample1
{
class Class1
{
/* Haftanin günlerini temsil edicek bir numaralandırıcı tipi olusturuyoruz.
Pazartesi 0 index değerine sahip iken Pazar 6 index değerine sahip
olucaktır.*/
enum Gunler
{
Pazartesi,
Sali,
Carsamba,
Persembe,
Cuma,
Cumartesi,
Pazar
}
static void Main(string[] args)
{
Console.WriteLine("Pazartesi gününün
degeri={0}",(int)Gunler.Pazartesi);
Console.WriteLine("Çarsamba günün
degeri={0}",(int)Gunler.Carsamba);
}
}
}
Burada Gunler yazdıktan sonra VS.NET ‘in intellisense özelliği sayesinde,
numaralandırıcının sahip olduğu değerlere kolayca ulasabiliriz.
Programı çalıstırıcak olursak asağıdaki ekran görüntüsünü elde ederiz:
Simdi baska bir örnek gelistirelim. Bu kez numaralandırıcının değerleri farklı olsun.
using System;
namespace enumSample
{
class Class1
{
enum Artis
{
Memur = 15,
Isci = 10,
Muhendis = 8,
Doktor = 17,
Asker = 12,
}
static void Main(string[] args)
{
Console.WriteLine("Memur maasi zam artis
orani={0}",(int)Artis.Memur);
Console.WriteLine("Muhendis maasi zam artis orani=
{0}",(int)Artis.Muhendis);
}
}
}
Dikkat edicek olursak, numaralandırıcıları program içinde kullanırken, açık olarak(explicit)
bir dönüsüm yapmaktayız. Su ana kadar numaralandırıcı elemanlarına integer değerler
atadık. Ama dilersek Long tipinden değer de atayabiliriz. Fakat bu durumda enum‘in
değer türünüde belirtmemiz gerekmektedir.Örnegin:
using System;
namespace enumSample
{
class Class1
{
enum Sınırlar: Long
{
EnBuyuk = 458796452135L,
EnKucuk = 255,
}
static void Main(string[] args)
{
Console.WriteLine("En üst sinir={0}",(long)Sinirlar.EnBuyuk);
Console.WriteLine("Muhendis maasi zam artis
orani={0}",(long)Sinirlar.EnKucuk);
}
}
}
Görüldüğü gibi Sınırlarlar isimli numaralandırıcı long tipinde belirtilmistir. Bu sayede
numaralandırıcı elemanlarına long veri tipinde değerler atanabilmistir. Dikkat edilecek bir
diğer nokta ise, bu elemanlara ait değerleri kullanırken, long tipine dönüstürme
yapılmasıdır.
Bir numaralandırıcı varsayılan olarak integer tiptedir. Bu nedenle integer değerleri olan
bir numaralandırıcı tanımlanırken int olarak belirtilmesine gerek yoktur.
Simdi daha çok ise yarar bir örnek gelistirmeye çalısalım. Uygulamamız son derece basit
bir forma sahip ve bir kaç satır koddan olusuyor. Amacımız numaralandırıcı kullanmanın
programcı açısından isleri daha da kolaylastırıyor olması. Uygulamamız bir Windows
Application. Form tasarımımız asağıdaki gibi olucak.
Form yüklenirken Sehir Kodlarının yer aldığı comboBox kontrolümüz otomatik olarak
numaralandırıcının yardımıyla doldurulucak. Iste program kodları:
using System;
namespace enumSample
{
class Class1
{
enum AlanKodu: Long
{
Anadolu=216,
Avrupa=212,
Ankara=312,
Izmir=412
}
private void Form1_Load(object sender, System.EventArgs e)
{
comboBox1.Items.Add(AlanKodu.Anadolu);
comboBox1.Items.Add(AlanKodu.Ankara);
comboBox1.Items.Add(AlanKodu.Avrupa);
comboBox1.Items.Add(AlanKodu.Izmir);
}
}
}
Đste sonuç:
Aslında bu comboBox kontrolünü baska sekillerde de alan kodları ile yükleyebiliriz. Bunu
yapmanın sayısız yolu var. Burada asıl dikkat etmemiz gereken nokta numaralandırıcı
sayesinde bu sayısal kodlarla kafamızı karıstırmak yerine daha bilinen isimler ile aynı
sonuca ulasmamızdır.
Geldik bir makalemizin daha sonuna. Đlerliyen makalelerimizde bu kez yine kendi değer
tiplerimizi nasıl yaratabileceğimize struct kavrami ile devam edeceğiz. Hepinize mutlu
günler dilerim.
Microsoft .NET Programlama Dilleri
Bu yazıda Microsoft dotNET ile birlikte programcılara sunulun gelistirme dilleri üzerinde
durulmustur. Bu yazı Temmuz 2003'te MSDN de yayınlanan Microsoft Programming
Languages yazısından Türkçe'ye çevrilmistir.
Microsoft .NET'in Yararları
Microsoft . NET Framework ,XML Web Servisi ve uygulamalarının derlenip çalıstırılması
için gerekli olan Microsoft Windows® bilesenlerini içerir. Bu; bize gelistirdiğimiz
uygulamalarda
• yüksek bir verim ,
• standart bir alt yapı,
• daha basit uygulama gelistirme ,
• çoklu dillerin(multilanguage) bulunduğu bir ortam ,
• var olan programlama bilgilerinden yararlanabilme,
• var olan uygulamalar ile kolay entegre olabilme,
• internet uygulamalarında kullanabilme ve çalıstırmanın rahatlığını getirir.
.NET Framework, iki ana bölümden olusur : CLR(Common Language Runtime) ve web
tabanlı programlamada devrimsel bir gelisme yaratan birlestirilmis sınıf
kütüphanesi(ASP.NET), akıllı istemci uygulamalarını gerçeklestirmek için Windows
formları, ortam ve temel veri girisleri için ADO.NET alt sistemi. Programcılar iki farkli dille
uygulama gelistirirken rahatlıkla .NET Framework kullanabilirler. Bu dillerin hepsi
(MSIL)'e derlenir ve daha sonra native(ana) koda dönüstürülür ve CLR ortamında
çalıstırılır. Bu sayede herhangi bir dille yazılan herhangi bir uygulama baska bir uygulama
ile kolaylıkla entegre olabilir. Bu ortamın programcılara sağladığı yarar sudur; islerini
yaparken kullanmaları için genis bir dil seçenegi vardir ve dolayısıyla programcılar en iyi
bildikleri dili seçebilirler.
Genis Bir Dil Seçeneği
Sanatçılar çalısırken bulundukları ortam ve araçlar, sanatçıların tecrübelerini ve
kisiliklerini yansıtır. Aynı yazılım uzmanları gibi onlarda bildikleri dili ve eğitimlerini göz
önüne alarak çalısırlar. Tüm yazılım uzmanlarını memnun edecek bir dil henüz yoktur.
Programcılar, doğustan farklı bir kimliktirler: kısmen bilim adamı, kısmen sanatçı, her
zaman dik kafalı, ve hep daha iyisini arastıran insanlardır. Buna rağmen modern
programlama dillerinin eksikliklerini kabul ederler.
"Biz her zaman, yarım milyon veya 50 milyon VB(Visual Basic) bilen programcıya sahip
olacağız. Bizim, .NET 'de VB miz var. Ve bizim simdi, . NET'de Java dilimiz ve hatta,
COBOL dilimiz bile var! Bunun ne demek oldugunu tahmin et? " -Tim Huckaby, baskan
ve CEO, Interknowlogy
Programlama dilleri personel alımında önemli bir faktördür. Bir yazılım uzmanının bildiği
dilden baska bir dili kullanmaya geçmesi zordur. .NET yapısının gücü ile bir kaç dili içinde
barındıran bir platform sağlar. Programcılar bu platform da-C++, Objective C, Fortran,
COBOL, Visual Basic®, Perl -'in her biri ile güçlü yazılım gelistirilebilirler.
Bu bölümde Microsoft'un dört farklı programlama dili sunduğunu ve bunları tek bir
ortamda nasıl birlestirdiğini göreceğiz.
• Visual Basic.NET , dünyanın en popüler gelistirme aracının ve dilinin en son
uyarlamasıdır. Visual Basic.NET, vazgeçilemez verimliligi teslim eden, ve
task(görev)-oriented(yönelimli) programlama için essiz özellikler sunan bir dildir.
• Visual C++ dili, power(güç)-oriented(yönelimli) güçlü uygulamalar gelistirilebilen ,
farkli teknolojilerle köprü kurabilen, hem windows doğal diline hemde(assembly)
.NET ara diline(IL) derlenebilen maksimum performans karekteristiklerine ve
yüksek fonksiyonaliteye sahip bir dil olarak karsımıza çıkar.
• Visual C #®.NET , modern ve yenilik getiren programlama dili ve gelistirme
aracıdır. Microsoft, C# ile bizi 2001'de tanıstırdı, C++ ve Java programcılarının
bildiği bir sentaks(sözdizimi) ile sunulmustu, bu yüzden C++ ve Java
gelistiricilerinin ilgisini çekmis olan C#, .NET Framework ile beraber kod
odaklı uygulamaları daha düzenli bir dil yapısı ile sunar.
• Visual J#®.NET , Microsoft . NET için Java-dili gelistirme aracidir. Visual J#.NET,
Visual J++ ve Java gelistiricilerine kendi dil ve söz dizimlerinden uzaklasmadan
.NET'in olanaklarindan tam olarak yararlanabilmeyi ve endüstrinin en gelismis XML
web servisleri platformundan faydalanabilmelerini saglamistir.
Burada Microsoft, programlama dillerindeki genis dil seçeneğinin gelistiricilere uygunluğu
ile dikkat çeker. Microsoft.NET, programcıları bu yeni platformda birlestirmek için eğitim
ve çözüme daha hızlı ulasmayı saglamada onlara yardım etme imkanı da sunar.
Visual Basic .NET
Visual Basic 1.0, Windows'un gelismesi ve daha genis bir kullanıcı sayısına ulasması ve
günümüzdeki verimliliğine kavusması için bir devrim yarattı. Böyle zengin bir tarihe sahip
olan VB , okunabilir bir syntax, sezgisel bir arayüzün ve aletlerin oldugu , taskoriented(
görev yönelimli) yapısı ile hızlı build edilebilmesi ile ve .Net ile yeni bir
yapılandırmaya kavusmustur. Visual Basic.NET dilinede diğer popular diller gibi her tür
Windows uygulaması, WEB,ve mobile aletler için uygulama yapabilme yetenekleri
eklendi.
Task-Orinted(görev yönelimli) Gelisme
Deadline(Son teslim) tarihleri, yazılım sanayisine yeni olan bir sey değildir. Programcıların
büyük bir grubu için, deadline tarihleri, günlük hayatın bir gerçeğidir. Programcılar
çoğunlukla, plan yaparken ,isin gereksinimlerini karsılayacak hızlı bir yolun çözümünü
ararlar. Bazi çözümler , bu ortamlar yaratılırken dikkatlice test edilecek, daha sonra
uygulama bu tarz yapılarda hemen kullanılacaktır. Uygulama gelistirme uzmanının
problemlerde çözüme odaklanması için bir dahaki is verilene kadar serbest bırakılması
gerekir. Task-Oriented bir gelistirme ortamı, ortam farklılıklarından programcıyı
kurtarmak için kabul edilebilir bir yöntemdir.
Hangi Programcılar Neden VB.NET'i Seçmelidir?
Gelecek kusak uygulamaları ve servisleri birlestirerek .NET ortamında arastırma yapmak
isteyen asağıdaki tipteki programcılar için Visual Basic.NET ideal bir dildir.
• .NET Framework ortamında hızlı ve üretken bir araçla uygulama gelistirmek
isteyen programcılar : .Net ile birlikte hizli ve rahat bir gelistirme araci sunulurken
Visual Basic.NET uygulamalarinda kolay sntax ve sezgiyle elde edilen gelistirme ortamini
sunar. Ayrıca VB.NET progracmıları ilgili kaynaklara çok hızlı bir sekilde erisebime
imkanına sahiptir.
• VB ile uygulama gelistirme tecrübesi olan programcılar : Visual Basic bilen
programcilar için Visual Basic.Net yapisina geçmek zor olmayacaktir. Visual Basic .NET
anahtar sözcükleri, sentaks ve derleme yapısı ile farklılıklar olustursa da, geleneksel VB
programcısına tanıdık gelecektir. VB.NET te ayrıca case-insensitivity, anlasılır kodlar ve
sentaksı vardır. Visual Basic'in ilk versiyonlarını kullananlarda .NET kullanmaya
yönelebilirler. Mevcut bulunan ActiveX kontrollerini de Visual Basic .NET te kullanmaya
devam edebilirler.
• Halihazırdaki ortamlara benzer tasarım zamanı ve kod editörü paradigmaları
arayan programcılar : Gelistiriciler bildik bir arayüz ve editor ararken, VB.NET ile
uygulamaların tasarımı drag and drop ile yapılabiliyor. Ayrıca otomatik, kolay
biçimlendirilmis bir kodlama sunar.
• Sezgisel ve erisebilirlik özelliği yüksek olan bir dille gelistirme yapmak isteyen
programcılar : Visual Basic.NET, gelistiricilerin büyük bir bölümüne ulasabilmek için
tasarlanmıstır. Bu nedenle hem uzmanlara, hem de yeni baslayanlara önerilir. Yeni
baslayanlar, Visual Basic ortamının ve Visual Basic dilinin birçok benzersiz özelligini
faydalı bulacaktır.
VB.NET Diline Has Özellikler
VB.NET, uygulama üretkenliğini hızlandıracak diğer .NET dillerinde olmayan bir takım
özellikleri içinde barındırır.
• Değiskenlere varsayılan ilk değer verme :VB.NET'te değiskenleri kullanılmadan
önce onlara ilk değer verilme zorunluluğu yoktur. Bu yüzden yeni baslayan bir çok
programcının diğer dillerde olduğu gibi kafası karısmayacaktır.
• Implicit typing(dolaylı tür belirtme) ve geç bağlama(late binding) :Visaul
Basic.Net te bir değisken kullanılmadan önce onun tipini belirtmek zorunda degiliz. Bu da
programcıya daha az eforla daha kullanısli kod yazmasında yardım eder.
• Numaralandırıcıların Davranısı :Visual Basic. NET,Enumeration tipleri kullanmak
gerektiginde .NET ortami sezgiyle bunu programciya getirir.
• Varsayılan public erisimi :Visual Basic.NET sınıfinın üyele elemanları varsayılan
olarak public olduğu için programcılara sezgisel gücü fazla olan bir özellik sunar.
• Shared üye elemanlarını kullanma :Shared(C# taki static) üye elemanlarına hem
sınıfın ismi üzerinden hem de ilgili sınıf nesnelerinden erisilebilir. Bu da programciya daha
esnek bir yapı sunar. Örneğin
Dim x as new MyClass
x.SharedMethod() ' ile birlikte
MyClass.SharedMethod()
ifadesi de geçerlidir.
• Opsiyonel parametreler : Visual Basic.NET programcıları nesne yönelimli
programlama tekniğinin bütün nüanslarını bilmeye gerek kalmadan esnek sınıf yapıları
tasarlayabilir. Örneğin sınıf tasarımcıları opsiyonel parametreleri kullanarak daha esnek
sınıflar tasarlayabilirler.
• Filtrelenmis catch blokları :VB.NET istisnai durumları ele elmada esnek bir yapı
sunar. Filtrelenmis catch blokları sayesinde programcılar hataları, hata numarasına göre,
istisnanının türüne göre yada herhangi bir kosullu ifadeye göre filtreleyebilirler.
• Parametreli Özellikler : Visual Basic.NET te özellikler C# taki karsılığından farklı
olarak parametreli olabilir. Böylece özellikler daha esnek bir yapı sunmus olur.
• Declarative event handlers : Visual Basic.NET'te olaylara ilisikin metotları bildirirken
Handles anahtar sözcüğü kullanılır.
• Arayüz üye elemanlarının yeniden bildirilmesi : VB.NET'te implemente edilen bir
arayüzün üye elemanının ismi arayüzü uygulayan sınıf tarafından değistirilebilir.
VB.NET Gelistirme Ortamına Has Özellikler
Visual Basic.NET programcılara daha çok yarar sağlayacak bir tasarıma, uygulama ve
servis yazmakta kolay bir ortam sağlar. Bu sadece Visual Basic.NET'i degil tüm .NET
platformunu kapsar.
Arka planda kodun derlenmesi : Gelistirme ortamı siz çalısırken arka planda kodunuzu
derler ve eğer kodda hata varsa bunu size listeler.
Visual Basic . NET otomatik olarak yazdığınız kodu düzenler ve kaydeder. Otomatik olarak
düzenlerken kodun durumu , anahtar sözcüklerin durumunu ve değiskenleri hizalayabilir.
Bu da çok fazla ifadenin kullanıldığı durumlarda yanlıs ifadelerin yada formatsız ifadelerin
düzgün görülmesini sağlar.
Performans
Son önemli nokta performanstır.Visual Basic . NET derleyicisinin ürettiği ara kod, C#
derleyicisinin ürettiği IL kodu ile aynı performansa sahiptir.
Visual C+ +
Çoğunlukla yazdığı programlardan güç beklentisi olan programcıların bu platformun tüm
özelliklerinden faydalanması mümkündür.CLR ve . NET altyapısının pek çok faydalarına
rağmen, bazı programcılar hala uygulamaları gelistirirken, Windows isletim sisteminin
tüm genislik ve derinliklerine güvenerek uygulamalarını olustururlar. Geleneksel olarak
programcılar, sistem verimliliğini en iyi kullanan kodu yazmak sistem tarafından sağlanan
kaynaklara(disk,hafıza) en etkili erisimi sağlamak amacıyla Visual C++ ortamını
seçmislerdir. Visual C++.NET bu geleneksel yöntemlerin devamını sağlamayı
hedeflemistir. Tabi bunu yaparken Win32 API den sıklıkla faydalanır. C++.NET aynı
zamanda .NET Framework ve yönetilebilir CLR nin bir çok imkanına erismeyi de sağlar.
Güç Yönelimli(Power-Oriented) Gelistirme
Bir çok durumda gelistiriciler, isletim sisteminin sağladığı bütün imkanlara erismek
isterler. Microsoft, bu imkanlardan soyutlanmıs yada tamamen bu imkanlar üzerine
kurulmus değisik araçlar tasarlamıstır. Bugün itibariyel .NET framework sağlam uygulama
gelistirmek için bu imkanların birçoğunu sunarken yinede isletim sisteminin bütün
yeteneklerini içinde barındırmaz. Güç yönelimli(Power-Oriented) gelistirme araçları,
programcılara bu dilin tüm özelliklerinin yanında, uygulamanın gerektirdiği çözümlerin de
kolayca çözüme kavusabileceği kütüphaneleri sağlar.
Hangi Programcılar Neden VB.NET'i Seçmelidir?
Visual C++, asağıdaki tipteki programcılar için ideal bir dildir.
• Win32 tabanlı bilesen ve uygulama gelistirmek isteyen programcılar :
Günümüzde programcıların bir grubu, native windows tabanlı uygulamalar yapmaya
ihtiyacı vardır. Bu programcılar bu tr uygulamalar için Win32 API yi ve native(doğal) C++
kütüphanelerini kullanırlar. Visual C++.NET 2003, bu tür uygulamaların performansını
daha iyi yönde etkileyebilrcek bir takım derleme parametrelerine sahiptir.
• Win32 tabanlı uygulamalar ve .NET ile gelistirilmis uygulamar arasındaki
bosluğu doldurmak isteyen programcılar : Halihazırda yazılmıs olan bir çok
uygulama, karmasık kodlar yüzünden, zaman, ücret veya bir çok nedenden dolayı .NET
Framework kullanılarak yeniden yazılamayabilir. Visual C++ ile programcılar, var olan
uygulamalarının genisleterek devamını .NET Framework çatısı altında gelistirebilirler.
Üstelik daha karmasık bir WinAPI altyapısını da kullanma imkanına kavusurlar. Microsoft
C# ve ya Visual Basic Windows API lerine erisimi sağlarken, C++'a karsı bir rakip olarak
tasarlanmadı.
• Uygulamaların ana olarak performansı ile ilgilenen programcılar : Uygulama
tasarımında ve çalıstırılmasında C++ gelistiriciye genis bir kontrol imkanı sunar. Đleri
düzey gelistiriciler C++ kullanarak diğer dillerde gelistirebilecekleri
uygulamalardan(native Windows yada .NET tabanlı) daha hızlı ve etkili çalısan uygulama
tasarlayıp implemente edebilirler.
• Farklı platformlar arasında çalısabilecek program gelistirmek isteyen
programcılar : Yalnızca C++ dili ISO standartlarını içerir ve gerçekten tasınabilir
sentaksı, her sistemde çalısabilecek bir yapı içerir. Visual C++.NET 2003'ün genisletilmis
standart uyumluluğunu sağlarken aynı zamanda programcılara ileri seviye dil özelliklerini
ve bir çok isletim sisteminde bulunan popüler sınıf kütüphanelerini kullanma imkanı
sunar.
C++.NET Diline Has Özellikler
Visual C++. NET, ileri düzey yazılımcılar tarafından büyük bir taleple karsılan kendine has
bir takım özelliklere sahiptir. Bu özelliklerin hepsi C++.NET'i .NET dilleri arasında en
güçlü kılmaya yetmektedir.
• Sablonlar(Templates) : Büyük ölçüde C++ diline has olan sablonlar yeniden
kullanılabilirliği ve performans artısı gibi bir çok önemli özelliği sağlamaktadır.
• Göstericiler(Pointers) : Göstericiler, C + + gelistiricilerine makinenin yerel hafızasına
doğrudan erisebilmesini sağlar ve böylece en yüksek seviyede performans elde edilir.
• Çoklu Türetme(Multiple Inheritance) : C++, hemen hemen tüm OOP desenlerini
implemente etmeyi sağlayan ve bütün OOP özelliklerini uygulamaya geçirecek özelliklere
sahiptir. Çoklu türetme de bu özelliklerden biridir.
• Intrinsics : Intrinsics'ler, geleneksel programlama tekniklerinde olmayan bir takım
yeni özelliklere erismeye imkan sunar. Örneğin MMX ve AMD 3D Now! registerları ve
komutları.
C++.NET Gelistirme Ortamına Has Özellikler
Visual C++.NET 2003 gelistirme ortamıda daha esnek ve daha gelismis uygulamalar
gelistirmek için bir takım özellikler sağlar.
• Derleyiciyi optimize etmek : Visual C++ derleyicisi bir çok gelistirme seneryosu için
derleme islemini optimize edebilir. Bu seneryolardan bir kaçı : MSIL üretimi, kodun
çalıscağı sisteme özgün optimizasyon, floating sayı hesaplamalarının yoğun olduğu
derlemeler.
• Çalısma zamanı kod güvenliği kontrolü : Programcılar kötü niyetli ataklara karsı
derleyicinin ileri seviye özelliklerini kullanarak daha güvenli Windows tabanlı uygulamalar
gelistirebilirler.
• 32 bit ve 64 bit desteği : Visual C++.NET derleyicisi 32 ve 64 bitlik Intel ve AMD
mikroislemcilerine ve yönelik gömülü mikrpislemcilere yönelik ölçeklenebilir kod
üretebilmektedir.
• Đleri düzey hata raporlama : Uygulamalar daima programcıların hatalarından
etkilenirler. Minidump teknolojisiyle Visual C++ gelistirme ortamı, uygulama gelistirme
uzmanlarına hataların kolayca belirlenmesine yardımcı olur. Üstelik derlenmis kodda bile
bu hatalar kolaylıkla rapor edilebilir.
• Gelismis hata ayıklama(debug) : Visual Studio hata ayıklayıcısı aynı anda hem
native hemde yönetilebilir kodda hata ayıklama desteğini benzer bir sekilde sunar.
Gelecekte, Visual C + + . NET Programcilara yardim etmek için daha güçlü özellikleri
içerecek.
• Generics : Parametreli kod algoritmalarını yeniden kullanılabilirliğini sağlamayı
hedefleyen çalısma zamanı(run-time) teknolojisi.
• Yönetilen(Manage) tiplerde sablonlar : Derleme zamanı C++ sablon sentaksının
yönetilen(manage) tipler içinde kullanılabilmesi.
Visual C#
Geleneksel Olarak Visual Basic ve Visual C + + farklı spektrumdaki programcıları
hedeflemistir. Visual Basic özellike üretkenliği ön plana çıkarması ile programcılara daha
kolay ve sezgisel bir gelistirme modeli sunuyordu. Öte yandan Visual C++, üretkenliği
azaltıyor gibi görünmesine rağmen Windows isletim sisteminin bütün özelliklerini etkili bir
sekilde kullanma imkanı tanıyordu.
Bu iki dilin sunduğu imkanlar arasındaki bosluğu doldurmak için Microsoft kod odaklı
uygulama gelistirmeyi modern ve yenilikçi bir tazrda ele alan C# dilini gelistirdi. C#, C++
sözdizimine benzer bir sekilde temiz ve güzel bir programlama dili sunarken aynı
zamanda Visual Basic dilinin üretkenliğinide korur.
Kod Odakli Gelistirme
Programcıların hepsi projelerinde mutlaka belli özelliklerde kod yazarlar. Fakat
programcılarinin çoğu zamanlarının önemli bir kısmını sihirbaz(wizard),kontrol ve tasarım
araçları kullanarak harcarlar ve böylece önemli ölçüde bir üretkenlik sağlarlar. Bu
özelliğin programcılar için tek kötü yanı sihirbaz tarafindan üretilen kodun
anlasılabilirliğinin az olmasıdır.Fakat programcılar kodlarında anlasılırlık ve verimlilik
arasindaki tercihlerinde güveni tercih ettiler.
Ayrıca kod odaklı gelistirme yapan programcılar baskaları tarafindan doğru tasarlanmıs
kodu yeniden yazmaya yönelir ve bu daha az bilgili programcıların pratik olarak iyi kod
gelistirebilmesindeki karmasıklığı düzeltir.
Hangi Programcılar Neden VB.NET'i Seçmelidir?
Gelecek kusak uygulamaları ve servisleri birlestirerek .NET ortamında arastırma yapmak
isteyen asağıdaki tipteki programcılar için C# ideal bir dildir.
• Üretkenlik arayan C/C++ ailesindeki diller ile gelistirme yapan programcılar
:C# dili, C++ dili gibi isleç(operator) asırı yüklerken buna ek olarak numaralandırmalar,
küçük-büyük harf duyarlılığı ve component-oriented(bilesen-yönelimli) özellikler olan
property,delegate, events ve daha fazlasini içerir. C#, .NET framework ile beraber yüksek
verimlilik ,yönetilebilir , daha güvenli ve anlasılabilir sözdizimi ile yeni özellikler isteyen
C++ programcılarına sunulmus bir dil olarak da düsünülebilir.
• Framework tasarımcıları ve yazılım mimarlari : Framework tarafindan iyi
desteklenen , isleç yükleme özellikleri içeren, güvenli olmayan kodlara ve önceden
yazılmıs yazılımlara erisimiyle C#, yazılım mimarlarına genis ve esnek kütüphaneler ve is
parçacıkları tasarlama imkanı sağlar.
• Java tabanlı yazılımlar gelistirmis programcılar : Java Language Conversion
Asistant (JLCA) ile Java programcıları uygulamalarını C# ve .NET Frameworke'e rahatlıkla
tasıyabilirler. JLCA kaynak kod seviyesinde bir analiz yapar ve Java kodunu C# koduna
dönüstürür. Dönüstürme islemi bittiğinde gelistiricinin dikkat etmesi gereken noktalar
belirtilir. Böylece tasıma islemi an az hasarla geçeklestirilmis olur.
C#'ın C ++ Diline Benzer olan Özellikleri
C# dili, geleneksel C++ özelliklerinin bir çoğunu desteklemektedir. Bu geleneksel
özelliklerin bir çoğu Visual C++ 'taki üretkenliği artırmak için de kullanılmıstır.
• Tüm CLR türleri için destek : C# dili tüm CLR veri tiplerini destekler , programcılar
çözüm sunarken .NET ortamının yönetilen çalısma ortamının tüm özelliklerinden
yararlanırlar.
• Referens yolu ile parametre aktarma ve out parametresi : C# programcıları,
parametreleri foksiyonlara referans yolu ile aktarabilirler ayrıca out parametresi ile
değiskenlere ilk değer vermeden onları fonksiyonlara parametre olarak geçirebilirler.
• Overloading (Operatör asırı yükleme) : Sınıf kütüphanesi tasarımcıları operatörleri
asırı yükleyerek daha sağlam sınıflar tasarlayabilirler.
• Using ifadesi : Programcılar, uygulamalarında bulunan kaynakları daha kontrollü bir
sekilde yönetebilmek için using anahtar sözcüğünü kullanırlar.
• Guvensiz kod(Unsafe code) : C#, programcılara gösterici kullanma imkanı tanıyarak
hafızaya direkt erisimi sağlar. Her ne kadar güvensiz kodda CLR yönetiminde olsa da ileri
düzey programcılar uygulamalarının hafıza yönetimi üzerinde söz sahibi olmaları için
güvensiz kod yazabirler. Buna rağmen hafıza üzerinde daha etkili bir kontrol için Visual
C++ kullanılması daha çok tavsiye edilir.
• XML dökümantasyonu : Programcılar, kodlarına açıklayıcı notlar eklemek için XML
formatındaki bildirimleri kullanabilirler.
Öte yandan C# dil tasarımının sınırlarını genisletecek sekilde hızla büyümektedir. C#
dilinin tasarımcıları yakın bir gelecekte dile eklemeyi planladıkları bir kaç önemli özellikten
bahsetmektedir. Daha modern ve yenilikçi bir yaklasım sunan bu özellikle sunlardır :
• Generics : Varolan kodların yeniden kullanılabilirliğini kolayca sağlayan C++
sablonlarına benzer bir yapı.
• Erisiciler (Iterators) : Koleksiyon tabanlı sınıfların elemanları arasında daha hızlı ve
kolay bir sekilde dolasmamızı sağlayacak yapı.
• Anonim(Anonymous) Metotlar : Basit görevleri temsilcilerle daha rahat bir sekilde
ele alacak yöntem
• Kısmi(partial) Türler : Bir kodu farklı dosyalara bölebilecek türler.
Visual J #
Microsoft, Visual J# dili ile JAVA dilini .NET ortamına sokmus oldu. Microsoft Java diline
.NET ortamının pratikliğini getirdi ve okullarda müfredatları olan programcılara,
ögrencilere, ve profesörlere Java yapısını muhafaza ederek onların .NET e hızlı bir sekilde
girmelerini sağladı. Ayırca J# dili windows tabanlı uygulama gelistiren Visual J++ 6.0
kullanıcılarına kolayca Visual J# .Net ortamına geçebilmelerinde kolaylıklar sagladi.
Java Gelistirme Ortamı
C++ gelistiricilerin sıkılıkla karsılastıkları problemler etkili ve kolay bir sentaks yapısı aynı
zamanda benzer OOP fonksiyonelitesi ile JAVA ile giderilmistir.Java ile uygulama
gelistirenlerin .NET ortamında uygulama gelistirenilmeleri için en uygun dil J# olarak
görülmektedir. Java programcıları dil değistirmek zorunda kalmadan .NET framework teki
bütün olanaklardan hızlı bir sekilde faydalanma imkanına kavusmustur.
Hangi Programcılar Neden VB.NET'i Seçmelidir?
Visual J#, asağıdaki tipteki programcılar için ideal bir dildir.
• Java-dili gelistiricileri : Daha önceden Java dilini kullanan bir programcı .NET e
geçerken bsska bir dili öğrenmek istemeyebilir. Visual J#, .NET platformunun getirdiği
özellikler ile java programcılarının bilgilerini kullanabildikleri rahat ve hızlı bir platform
sunar.
• Visual J++ ile kod gelistiren programcılar : Visual J# ortamı varolan Visual J++
uygulamalarını .NET ortamına sorunsuzca tasıyabilir ve böylece Visual J#.NET kullanmaya
baslayan programcılar projelerinde .NET alt yapısının getirdiği pratikliği ve rahatlığı
hemen farkederler.
• Ögrenciler, ögretmenler, ve profösörler : Öğrenciler ve öğretmenler Computer
Science derslerinde Java dilinin basitliğinden faydalanmak için Visual J#.NET dilini
kullanabilirler. Visual J#.NET, ileri bilgisayar bilimin bütün gerekliliklerini karsılar.
J#.NET Diline Has Özellikler
Bir çok dilde bulunan özelliklerin çogunu yapısında içeren J#, daha rahat ve bildik yapısı
ile deneyimli Java gelistiricileri için .Net Framework'e yönelik uygulamalar gelistirmek için
ideal bir dildir.
• Java dilinin söz dizimi : Java gelistiricileri bildik bir dil yapısı ile karsılacak ve aynı
zaman .NET in tüm imkanlarından faydalandıklarını görecekler.
• Sınıf kütüphanesi desteği : Bağımsız olarak gelistirilen ve bir çok özelliği sunan Java
1.1.4 JDK versiyonundaki kütüphane ile JDK 1.2 java.util de bulunan hemen hemen
bütün sınıfları içerir.
• Özellikler, temsilciler(delegates) ve olaylar(events) : .NET geleneksel JAVA söz
dizimi ile .NET'in güçlü özelliklerinden olan event ,delegate, ve property yapılarını
destekler.
• Javadoc Yorumları : J#, Javadoc kolarındaki yorumlama stilini destekler. Visual J# .
NET, kullanıcıların HTML API belgesini yaratabilmesine olanak kılar.
Visual J#.NET Gelistirme Ortamına Has Özellikler
Visual J#.NET direkt olarak Visual Studio.NET gelistirme ortamına entegre bir sekilde
çalısır. Dolayısıyla tasarlama araçları, editörler ve hata ayıklayıcılar Visual J# gelistirme
ortamında rahatlıkla kullanılabilir. Ayrıca hazlihazırdaki JAVA programcılarının .NET'e
geçisini kolaylastıracak bir takım araçlar da vardır.
• Visual J++ Upgrade Wizard : Visual J++ gelistiricileri projelerini Visual J# ortamı
için upgrade edebilirler. Bu sihirbaz proje dosyalarını çevirir ve olası potansiyel sorunlar
için kullanıcıyı bilgilendirir.
• Ikili dönüstürücü : Bu araç, Java byte kodunu, .NET uygulamalarında kullanmak
üzere MS.NET assembly lerine dönüstürür.
Özet
Programlama dilleri fakli çözümler için kullanılabilmektedir. Her dil kendi özelliklerini ve
belirli bir uygulamanın ihtiyaçlarını karsılayabilecek en uygun ortamı içerir. Microsoft
genis bir dil seçeneğini sunduğu gelismis .NET yapısı ile yazılım uygulamalarında daha
sağlam ve fonksiyonalite sağlamıs bulunmakta.
C#'ta Params ile Değisken Sayıda Parametre ile Çalısma
Bu makalemizde, C# metodlarında önemli bir yere sahip olduğunu düsündüğüm params
anahtar kelimesinin nasıl kullanıldığını incelemeye çalısacağız. Bildiğiniz gibi metodlara
verileri parametre olarak aktarabiliyor ve bunları metod içersinde isleyebiliyoruz. Ancak
parametre olarak geçirilen veriler belli sayıda oluyor. Diyelimki sayısını bilmediğimiz bir
eleman kümesini parametre olarak geçirmek istiyoruz. Bunu nasıl basarabiliriz? Đste
params anahtar sözcüğü bu noktada devreye girmektedir. Hemen çok basit bir örnek ile
konuya hızlı bir giris yapalım.
using System;
namespace ParamsSample1
{
class Class1
{
/* burada Carpim isimli metodumuza, integer tipinde değerler geçirilmesini
sağlıyoruz. params anahtarı bu metoda istediğimiz sayıda integer değer
geçirebileceğimizi ifade ediyor*/ public int Carpim(params int[]
deger)
{
int sonuc=1;
for(int i=0;i<deger.Length;++i) /*Metoda gönderilen elemanlar doğal olarak bir
dizi olustururlar. Bu dizideki elemanlara bir for döngüsü ile kolayca erisebiliriz. Dizinin
eleman sayısını ise Length özelliği ile öğreniyoruz.*/
{
sonuc*=deger[i]; /* Burada metoda geçirilen integer değerlerin birbirleri ile
çarpılmasını sağlıyoruz*/
}
return sonuc;
}
static void Main(string[] args)
{
Class1 cl=new Class1();
Console.WriteLine("1*2*3*4={0}",cl.Carpim(1,2,3,4)); /* Burada Carpim isimli
metoda 4 integer değer gönderdik. Asağıdaki kodda ise 2 adet integer değer
gönderiyoruz.*/
Console.WriteLine("8*5={0}",cl.Carpim(8,5));
Console.ReadLine();
}
}
}
Bu örneği çalıstıracak olursak, asağıdaki sonucu elde ederiz.
Sekil 1. Ilk Params Örneğinin Sonucu
Peki derleyici bu islemi nasıl yapıyor birazda ondan bahsedelim. Carpim isimli metoda değisik sayılarda parametre
gönderdiğimizde, derleyici gönderilen paramtetre sayısı kadar boyuta sahip bir integer dizi olusturur ve du dizinin
elemanlarına sırası ile (0 indexinden baslayacak sekilde) gönderilen elemanları atar. Daha sonra aynı metodu bu
eleman sayısı belli olan diziyi aktararak çağırır. cl.Carpim(8,5) satırını düsünelim; derleyici,
Đlk adımda,
int[] dizi=new int[2] ile 2 elemanlı 1 dizi yaratır.
Đkinci adımda,
dizi[0]=8
dizi[1]=5 seklinde bu dizinin elemanlarını belirler.
Son adımda ise metodu tekrar çağırır.
cl.Carpim(dizi);
Bazı durumlarda parametre olarak geçireceğimiz değerler farklı veri tiplerine sahip olabilirler. Bu durumda params
anahtar sözcüğünü, object tipinde bir dizi ile kullanırız. Hemen bir örnek ile görelim. Aynı örneğimize Goster isimli
değer döndürmeyen bir metod ekliyoruz. Bu metod kendisine aktarılan değerleri console penceresine yazdırıyor.
public void Goster(params object[] deger)
{
for(int i=0;i<deger.Length;++i)
{
Console.WriteLine("{0}. değerimiz={1}",i,deger[i].ToString());
}
Console.ReadLine();
}
static void Main(string[] args)
{
cl.Goster(1,"Ahmet",12.3F,0.007D,true,599696969,"C");
}
Görüldüğü gibi Goster isimli metodumuza değisik tiplerde(int,Float,Decimal,bool, String) parametreler
gönderiyoruz. Đste sonuç;
Sekil 2. params object[] kullanımı.
Simdi dilerseniz daha ise yarar bir örnek üzerinde konuyu pekistirmeye çalısalım. Örneğin değisik sayıda tabloyu
bir dataset nesnesine yüklemek istiyoruz. Bunu yapıcak bir metod yazalım ve kullanalım. Programımız, bir sql
sunucusu üzerinde yer alan her hangibir database’e bağlanıp istenilen sayıdaki tabloyu ekranda programatik
olarak olusturulan dataGrid nesnelerine yükleyecek. Kodları inceledikçe örneğimizi daha iyi anlıyacaksınız.
Sekil 3. Form Tasarımımız
Uygulamamız bir Windows Application. Bir adet tabControl ve bir adet Button nesnesi içeriyor. Ayrıca params
anahtar sözcüğünü kullanan CreateDataSet isimli metodumuzu içeren CdataSet isimli bir class’ımızda var. Bu
class’a ait kodları yazarak isimize baslayalım.
using System;
using System.Data;
using System.Data.SqlClient;
namespace CreateDataSet
{
public class CDataSet
{
/* CreateDataSet isimli metod gönderilen baglantiAdi stringinin değerine göre bir
SqlConnection nesnesi olusturur. tabloAdi ile dataset nesnesine eklemek istediğimi tablo
adlarini bu metoda göndermekteyiz. params anahtarı kullanıldığı için istediğimiz sayıda
tablo adı gönderebiliriz. Elbette, geçerli bir Database ve geçerli tablo adları
göndermeliyiz.*/
public DataSet CreateDataSet(string baglantiAdi,params string[] tabloAdi)
{
string sqlSelect,conString;
conString="data source=localhost;initial catalog="+baglantiAdi+";integrated
security=sspi"; /* Burada SqlConnection nesnesinin kullanacağı connectionString'i
belirliyoruz.*/
DataSet ds=new DataSet();/* Tablolarimizi tasıyacak dataset nesnesini
olusturuyoruz*/
SqlConnection con=new SqlConnection(conString); /*SqlConnection nesnemizi
olusturuyoruz*/
SqlDataAdapter da;/* Bir SqlDataAdapter nesnesi belirtiyoruz ama henüz
olusturmuyoruz*/
/*Bu döngü gönderdiğimiz tabloadlarını alarak bir Select sorgusu olusturur ve
SqlDataAdapter yardımıyla select sorgusu sonucu dönen tablo verilerini olusturulan bir
DataTable nesnesine yükler. Daha sonra ise bu DataTable nesnesi DataSet nesnemizin
Tables kolleksiyonuna eklenir. Bu islem metoda gönderilen her tablo için yapılacaktır.
Böylece döngü sona erdiğinde, DataSet nesnemiz göndermis olduğumuz tablo adlarına
sahip DataTable nesnelerini içermis olucaktır. */
for(int i=0;i<tabloAdi.Length;++i)
{
sqlSelect="SELECT * FROM "+tabloAdi[i];
da=new SqlDataAdapter(sqlSelect,con);
DataTable dt=new DataTable(tabloAdi[i]);
da.Fill(dt);
ds.Tables.Add(dt);
}
return ds; /* Son olarak metod çağırıldığı yere DataSet nesnesini
göndermektedir.*/
}
public CDataSet()
{
}
}
}
Simdi ise btnYukle isimli butonumuzun kodlarını yazalım.
private void btnYukle_Click(object sender, System.EventArgs e)
{
CDataSet c=new CDataSet();
DataSet ds=new DataSet();
ds=c.CreateDataSet("northwind","Products","Orders");
for(int i=0;i<ds.Tables.Count;++i)
{
/* tabControl'umuza yeni bir tab page ekliyoruz.*/
tabControl1.TabPages.Add(new
System.Windows.Forms.TabPage(ds.Tables[i].TableName.ToString()));
/* Olusturulan bu tab page'e eklenmek üzere yeni bir datagrid olusturuyoruz.*/
DataGrid dg=new DataGrid();
dg.Dock=DockStyle.Fill;/*datagrid tabpage'in tamamını kaplıyacak*/
dg.DataSource=ds.Tables[i]; /* DataSource özelliği ile DataSet te i indexli
tabloyu bağlıyoruz.*/
tabControl1.TabPages[i].Controls.Add(dg);/* Olusturduğumuz dataGrid nesnesini
TabPage üstünde göstermek için Controls koleksiyonunun Add metodunu
kullanıyoruz.*/
}
}
Simdi programımızı çalıstıralım. Đste sonuç;
Sekil 4. Tabloların yüklenmesi.
Görüldüğü gibi iki tablomuzda yüklenmistir. Burada tablo sayısını arttırabilir veya azaltabiliriz. Bunu params
anahtar kelimesi mümkün kılmaktadır. Örneğin metodomuzu bu kez 3 tablo ile çağıralım;
ds=c.CreateDataSet("northwind","Products","Orders",”Suppliers”);
Bu durumda ekran görüntümüz Sekil 5 teki gibi olur.
Sekil 5. Bu kez 3 tablo gönderdik.
Umuyorumki params anahtar sözcüğü ile ilgili yeterince bilgi sahibi olmussunuzdur. Bir sonraki makalemizde
görüsmek dileğiyle hepinize mutlu günler dilerim.
C#'ta Özyenilemeli Algoritmalar (Recursion)
Özyenilemeli algoritmalar tüm dünyada bilgisayar bilimleriyle ilgili bölümlerde veri
yapıları ve algoritmalar derslerinde detaylı olarak incelenir. Bu bağlamda biz de
makalemizde özyenilemeli algoritmaları gelistirmeyi ve C# ile kodlamayı öğreneceğiz.
Önce konunun teorik temelleri üzerinde duracağız. Daha sonra daha iyi anlasılabilmesi
için konu ile ilgili örnekler yapacağız. Makaleyi bitirmeden önce ise klasik döngüler ve
özyenilemeli algoritmaları karsılastıracağız.
Bir alogirtma gelistirirken genelde döngüleri ve karar mekanizmalarını metodların içinde
kullanırız. Fakat bazı durumlarda döngüler yerine özyenilemeli algoritmalar kullanmak
daha kolay ve anlasılır olabilir. Özyenilemeli (recursive) metodların püf noktası bu tür
metodların bir sekilde tekrar tekrar kendilerini çağırmalarıdır.
Özyenilemeli algoritmalarda problemin en basit hali için çözüm bulunur. Bu en basit
duruma temel durum (base case) denir. Eğer metod temel durum için çağırılırsa
sonuç dönderilir Daha karmasık durumlar için metod, temel durumdan yararlanılarak,
problemi çözmeye çalısır yani kendini çağırır. Karmasık durumlar için yapılan her çağrı
recursion step olarak adlandırılır.
Đsterseniz konunun kafanızda daha iyi canlanması için klasik faktoriyel örneğiyle devam
edelim. Sıfırdan büyük herhangi bir n tamsayısının faktoriyelinin formülü sudur:
n! = n * (n-1) * (n-2) * .... * 3 . 2 . 1
Ayrıca 0! ve 1!'in değerleri bir olarak tanımlanmıstır. Mesela 5! = 4*3*2*1 =
120'dir.
Bir sayının, mesela n, faktoriyelini özyenilemeli değilde döngü kullanarak bulmak istersek
asağıdakine benzer bir kod kullanabiliriz:
int faktoriyel = 1;
for( int i = n; i >= 1; i-- )
faktoriyel *= i;
Kod 1: Döngü ile Faktoriyel hesabı
Eğer bu problemi özyenilemeli algoritma yardımıyla çözmek istersek su noktaya dikkat
etmemiz gerekiyor:
n! = n * (n-1)!
Daha açık bir yazım ile;
5! = 5 * 4 * 3 * 2 * 1
5! = 5 * (4 * 3 * 2 * 1)
5! = 5 * (4!)
Asağıda sekilde 5!in özyenilemeli bir algoritmada nasıl hesablanacağını görüyoruz. Seklin
solunda 5!'den 1!'le kadar her özyenilemeli çağrıdaki neyin çağrılacağı sağda ise sonuca
ulasılana kadar her çağrıda dönen değerler yeralıyor.
C# diliyle özyenilemeli biçimde Faktoriyel hesabı yapan bir metodu asağıdaki gibi
yazabiliriz. Bu fonksiyona int tipinde sayi isimli bir değisken geçiriyoruz. Eğer sayi 1'den
küçük veya esit ise, ki bu temel durumdur, fonksiyon 1 değerini dönderiyor. Diğer
durumlarda ise fonksiyonumuz
sayi * Faktoriyel(sayi-1)
değerini dönderiyor.
private static long Faktoriyel(int sayi)
{
if( sayi <= 1 ) // Eğer temel durumsa 1 dönder
return 1;
else // Temel durum değilse n * (n -1)! bul.
return sayi * Faktoriyel( sayi-1 );
}
Kod 2: Özyenileme ile Faktoriyel hesabı
Sıra örneğimizi bir Windows uygulaması olacak biçimde programlayalım. Bunun için
öncelikle asağıda gördüğümüz Form'u tasarlayalım. Metin kutusuna txtSayi ve düğmeye
btnHesapla isimleri vermeyi unutmayalım.
Formdaki btnHesapla isimli düğmeye çift tıklayalım düğmenin Click olayına cevap veren
metodu yazalım.
private void btnHesapla_Click(object sender, System.EventArgs e)
{
string sonucMetin="";
// metin kutusundan değeri al ve int tipine çevir:
int sayi = Convert.ToInt32(txtSayi.Text);
for(int i=1; i<= sayi; i++)
sonucMetin += i + "!= \t" + Faktoriyel(i).ToString() + "\n";
MessageBox.Show(sonucMetin.ToString(),"Faktoriyel Hesabı");
}
Kod 3: Örnek programda btnHesapla_Click() metodu
Yukarıdaki metod içinde metin kutusuna girilen sayi değerine kadar olan tüm
faktoriyeller hesaplanıp ekrana mesaj kutusu içinde yazdırılıyor. Programımızı
çalıstırmadan önce programımıza Kod 2'de yeralan metodu da eklemeyi unutmayınız.
Örneğimizi 10 değeri için çalıstırırsak asağıdaki sonucu elde ederiz:
Özyenilemeli Algoritma Örneği: Fibonacci Serisi
Lise ve üniversitelerde matematik derslerinde seriler konusunda gösterilen Fibanocci
serilerini programlamak için döngüler yerine özyenilemeli algoritma kullanmak daha kolay
ve anlasılır oluyor. Bu serinin tanımı ise herhangi bir n sayısı için Fibonacci(n)'nin değeri
Fibonacci(n-1) ve Fibonacci(n-2)'nin seklindedir. Tabiki Fibonacci(0) ve Fibonacci(1)'in
değeri 1 olarak alınıyor.
Bu serideki sayılar doğada çok sayıda bulunur ve spiral seklini olustururlar. Ayrıca art
arda gelen iki Fibonacci değerinin oranı 1.68.. seklindedir. Bu değer altın oran olarak
adlandırılır. Özellikle kart postallar, tablolar vb nesnelerin boy ve enlerinin oranları da
1.68.. gibidir. Çünkü insan gözüne çok hos görünürler. Neyse konumuza devam edelim
isterseniz...
Yukarıdaki tanımlardan yola çıkarak Fibonacci serisini hesaplayan metod'ta iki temel
durum olacaktır. Bunlar Fibonacci(0) = 1 ve Fibonacci(1) = 1. Ayrıca diğer durumlar için
dönen değer, herhangi bir n için, Fibonacci(n-1) ve Fibonacci(n-2)'nin toplamıdır. O
zaman metodumuz asağıdaki gibi olacaktır:
private static long Fibonacci(int sayi)
{
if( sayi == 0 || sayi == 1) // Eğer temel durumlardan biriyse 1 dönder
return 1;
else // Temel durum değilse (n-1) + (n -2) bul.
return Fibonacci( sayi-1 ) + Fibonacci( sayi-2 );
}
Kod 4: Özyenileme ile Fibonacci serisi hesabı
Fibonacci serisi ile ilgili asağıdaki küçük formu tasarlayalım. Sonra gerekli kodları yazıp
örneğimizi deneyelim. Formdaki metin kutusunun ismi yine txtSayi olsun. Ayrıca Hesapla
etiketine sahip düğmenin ismi btnHesapla olsun. Son olarak arka planı koyu kırmızı olan
etiketin ismi de label2 olacak.
Programı tamamlamak için Hesapla düğmesinini tıklandığında gerekli isleri yapacak kodu
yazmaya geldi. Ayrıca programın kodunun içine Kod 4 yeralan fonksiyonu da ekleyiniz.
private void btnHesapla_Click(object sender, System.EventArgs e)
{
// Kullanıcını girdiği değeri al ve int tipine çevir.
int sayi = Convert.ToInt32(txtSayi.Text);
// Fibonacci Hesabı yapan fonksiyonu girilen sayi değeri ile çağır.
// Sonucu label2'ye yazdır.
label2.Text =Fibonacci(sayi).ToString();
}
Kod 5: Fibonacci örneğinde btnHesapla_Click() metodu
Programı test etmek için Fibonacci(15) değerini bulmak istersek asağıdaki sonucu elde
ederiz.
Özyenilemeli Algoritma Örneği: Fibonacci Serisi
Makalemizi bitirmeden önce özyenilemeli algoritmalar ile döngülerden olusan
algoritmaların aralarındaki farklara ve benzerlikte gözatmakta yarar olduğu kanısındayım.
Đki algoritma türü de akıs kontrol mekanizmlarını kullanır. Döngülerde for, while ve do
while yapıları kullanılırken özyenilemeli algoritmalarda if, if/else ve switch yapıları
yeralır. Aslında hem döngüler de hem de özyenilemeli metodlarda itereasyonlar bulunur.
Döngüler doğaları gereği açık bir biçimde iteratiftirler. Fakat özyenilemeli algoritmalarda
iterasyon aynı metodun tekrar tekrar kendi içinden çağrılması ile gerçeklesir. Döngülü ve
özyenilemeli algoritmalarda göze çarpan diğer bir benzerlikte sonladırıcı testlerin
bulunmasıdır. Döngülerde sayacın(counter) belli bir değere ulasıp ulasmadığı kontrol
edilir ve gerekirse döngü sonlanır. Özyenilemeli algoritmalarda o andaki durumun temel
durum olup olmamasına göre islemler devam edebilir veya sonlanabilir. Son olarak hem
döngülerle hem de özyenilemeli algoritmalar ile bilgisayarı sonsuz döngüye(infinite loop)
sokabiliriz. Birincisi döngünün sonlanacağı sayaca ulasmanın imkansız olmasından ikincisi
ise temel duruma ulasamamaktan kaynaklanır.
Aslında özyenilemeli algoritmaları kullanırsak hem daha yavas hem de hafızada daha çok
yer kaplayan programlar yazmıs oluruz. Fakat çoğu zaman aynı problemin çöümünü
özyenilemeli olarak bulmak daha kolaydır. Ya da döngülerle aynı sonuca varacak
algoritmayı düsünmek zor olur. Ayrıca özyenilemeli algoritmaları inceleyince anlamak ve
hata ayıklamak daha kolaydır. Bu durumda seçim programcıya kalmıstır. Yalnız
islemcilerin giderek hızlanması, hafıza fiyatlarındaki düsüsü ve programın daha kolay
yönetilebilmesinin getirdiği avantajları göz önüne almanızı tavsiye ederim.
Bu makalede özyenilemeli algoritmaları detayları ile inceledik. Konu ile ilgili sorularınızı
rahatlıkla sorabileceğinizi belirterek makalemize son verelim.
Struct(Yapı) Kavramı ve Class(Sınıf) ile Struct(Yapı) Arasındaki
Farklar
Bu makalemizde struct kavramını incelemeye çalısacağız. Hatırlayacağınız gibi, kendi
tanımladığımız veri türlerinden birisi olan Numaralandırıcıları (Enumerators) görmüstük.
Benzer sekilde diğer bir veri tipide struct (yapı) lardır.
Yapılar, sınıflar ile büyük benzerleklik gösterirler. Sınıf gibi tanımlanırlar. Hatta sınıflar
gibi, özellikler,metodlar,veriler, yapıcılar vb... içerebilirler. Buna karsın sınıflar ile yapılar
arasında çok önemli farklılıklar vardır.
Herseyden önce en önemli fark, yapıların değer türü olması ve sınıfların referans türü
olmasıdır. Sınıflar referans türünden oldukları için, bellekte tutulus biçimleri değer
türlerine göre daha farklıdır. Referans tiplerinin sahip olduğu veriler belleğin öbek(heap)
adı verilen tarafında tutulurken, referansın adı stack(yığın) da tutulur ve öbekteki
verilerin bulunduğu adresi isaret eder. Ancak değer türleri belleğin stack denilen kısmında
tutulurlar. Asağıdaki sekil ile konuyu daha net canlandırabiliriz.
Sekil 1. Referans Tipleri
Asağıdaki sekilde ise değer tiplerinin bellekte nasıl tutulduğunu görüyorsunuz.
Sekil 2. Değer Tipleri
Đste sınıflar ile yapılar arasındaki en büyük fark budur. Peki bu farkın bize sağladığı
getiriler nelerdir? Ne zaman yapı ne zaman sınıf kullanmalıyız? Özellikle metodlara veriler
aktarırken bu verileri sınıf içerisinde tanımladığımızda, tüm veriler metoda aktarılacağını
sadece bu verilerin öbekteki baslangıç adresi aktarılır ve ilgili parametrenin de bu
adresteki verilere isaret etmesi sağlanmıs olur. Böylece büyük boyutlu verileri stack'ta
kopyalayarak gereksiz miktarda bellek harcanmasının önüne geçilmis olunur. Ancak
küçük boyutlarda veriler ile çalısırken bu verileri sınıflar içerisinde kullandığımızda bu
kezde gereksiz yere bellek kullanıldığı öbek siser ve performans düser. Bu konudaki
uzman görüs 16 byte'tan küçük veriler için yapıların kullanılması, 16 byte'tan büyük
veriler için ise sınıfların kullanılmasıdır.
Diğer taraftan yapılar ile sınıflar arasında baska farklılıklarda vardır. Örneğin bir yapı için
varsayılan yapıcı metod (default constructor) yazamayız. Derleyici hatası alırız. Ancak bu
değisik sayıda parametreler alan yapıcılar yazmamızı engellemez. Oysaki sınıflarda
istersek sınıfın varsayılan yapıcı metodunu kendimiz yazabilmekteyiz.
Bir yapı içersinde yer alan constructor metod(lar) içinde tanımlamıs olduğumuz alanlara
baslangıç değerlerini atamak zorundayız. Oysaki bir sınıftaki constructor(lar) içinde
kullanılan alanlara baslangıç değerlerini atamaz isek, derleyici bizim yerimize sayısal
değerlere 0, boolean değerlere false vb... gibi baslangıç değerlerini kendisi otomatik
olarak yapar. Ancak derleyici aynı isi yapılarda yapmaz. Bu nedenle bir yapı içinde
kullandığımız constructor(lar)daki tanımlamıs olduğumuz alanlara mutlaka ilk değerlerini
vermemiz gerekir. Ancak yinede dikkat edilmesi gereken bir nokta vardır. Eğer yapı
örneğini varsayılan yapılandırıcı ile olusturursak bu durumda derleyici yapı içinde
kullanılan alanlara ilk değerleri atanmamıs ise kendisi ilk değerleri atar. Unutmayın,
parametreli constructorlarda her bir alan için baslangıç değerlerini bizim vermemiz
gerekmektedir. Örneğin, asağıdaki Console uygulamasını inceleyelim.
using System;
namespace StructSample1
{
struct Zaman
{
private int saat,dakika,saniye;
private string kosucuAdi;
public string Kosucu
{
get
{
return kosucuAdi;
}
set
{
kosucuAdi =value;
}
}
public int Saat
{
get
{
return saat;
}
set
{
saat =value;
}
}
public int Dakika
{
get
{
return dakika;
}
set
{
dakika =value;
}
}
public int Saniye
{
get
{
return saniye;
}
set
{
saniye =value;
}
}
}
class Class1
{
static void Main (string[] args)
{
Zaman z;
Console.WriteLine ("Kosucu:"+z.Kosucu);
Console.WriteLine ("Saat:"+z.Saat.ToString());
Console.WriteLine ("Dakika:"+z.Dakika.ToString());
Console.WriteLine ("Saniye:"+z.Saniye.ToString());
}
}
}
Yukarıdaki kod derlenmeyecektir. Nitekim derleyici "Use of unassigned local variable
'z'" hatası ile z yapısı için ilk değerlerin atanmadığını bize söyleyecektir. Ancak z isimli
Zaman yapı türünü new anahtarı ile tanımlarsak durum değisir.
Zaman z;
Satırı yerine
Zaman z=new Zaman ();
yazalım . Bu durumda kod derlenir. Uygulama çalıstığında asağıdaki ekran görüntüsü ile
karsılasırız. Görüldüğü gibi z isimli yapı örneğini new yapılandırıcısı ile tanımladığımızda,
derleyici bu yapı içindeki özelliklere ilk değerleri kendi atamıstır. Kosucu isimli özellik için
null, diğer integer özellikler için ise 0.
Sekil 3. New yapılandırıcısı ile ilk değer ataması.
Yine önemli bir farkta yapılarda türetme yapamıyacağımızdır. Bilindiği gibi bir sınıf
olusturduğumuzda bunu baska bir temel sınıftan kalıtım yolu ile türetebilmekteyiz ki
inheritance olarak geçen bu kavramı ilerliyen makalelerimizde isleyeceğiz. Ancak bir
yapıyı baska bir yapıyı temel alarak türetemeyiz. Simdi yukarıda verdiğimiz örnekteki
yapıdan baska bir yapı türetmeye çalısalım.
struct yeni:Zaman
{
}
satırlarını kodumuza ekleyelim. Bu durumda uygulamayı derlemeye çalıstığımızda
asağıdaki hata mesajını alırız.
'Zaman' : type in interface list is not an interface
Bu belirgin farklılıklarıda inceledikten sonra dilerseniz örneklerimiz ile konuyu
pekistirmeye çalısalım.
using System;
namespace StructSample1
{
struct Zaman
{
private int saat,dakika,saniye;
private string kosucuAdi;
/* Yapı için parametreli bir constructor metod tanımladık. Yapı içinde yer alan kosucuAdi,saat,dakika,saniye
alanlarına ilk değerlerin atandığına dikkat edelim. Bunları atamassak derleyici hatası alırız. */
public Zaman(string k,int s,int d,int sn)
{
kosucuAdi =k;
saat =s;
dakika =d;
saniye =sn;
}
/* Bir dizi özellik tanımlayarak private olarak tanımladığımız asıl alanların kullanımını kolaylastırıyoruz. */
public string Kosucu
{
get
{
return kosucuAdi;
}
set
{
kosucuAdi =value;
}
}
public string Saat
{
get
{
return saat;
}
set
{
saat =value;
}
}
public string Dakika
{
get
{
return dakika;
}
set
{
dakika =value;
}
}
public string Saniye
{
get
{
return saniye;
}
set
{
saniye =value;
}
}
}
class Class1
{
static void Main (string[] args)
{
/* Zaman yapısı içinde kendi yazdığımız parametreli constuructorlar ile Zaman yapısı örnekleri
olusturuyoruz. Yaptığımız bu tanımlamarın ardından belleğin stack bölgesinde derhal 4 adet değisken olusturulur
ve değerleri atanır. Yani kosucuAdi,saat,dakika,saniye isimli private olarak tanımladığımız alanlar bellekte stack
bölgesinde olusturulur ve atadığımız değerleri alırlar. Bu olusan veri dizisinin adıda Zaman yapısı tipinde olan
Baslangic ve Bitis değiskenleridir. */
//
Zaman Baslangic=new Zaman ("Burak",1,15,23);
Zaman Bitis=new Zaman ("Burak",2,20,25);
/* Zaman yapısı içinde tanımladığımız özelliklere erisip islem yapıyoruz. Burada elbette
zamanları birbirinden bu sekilde çıkarmak matematiksel olarak bir cinayet. Ancak amacımız yapıların kullanımını
anlamak. Bu satırlarda yapı içindeki özelliklerimizin değerlerine erisiyor ve bunların değerleri ile sembolik islemler
yapıyoruz */
int saatFark=Bitis.Saat-Baslangic.Saat;
int dakikaFark=Bitis.Dakika-Baslangic.Dakika;
int saniyeFark=Bitis.Saniye-Baslangic.Saniye;
Console.WriteLine ("Fark {0} saat, {1} dakika, {2} saniye",saatFark,dakikaFark,saniyeFark);
}
}
}
Bir sonraki makalemizde görüsmek dileğiyle. Hepinize mutlu günler dilerim
Windows 98'de C# Kodu Derleyin
Windows 9x isletim sistemlerine dotNET Framework kurularak, dotNET platformunda
yazılmıs programlar çalıstırılabiliyor, fakat siz 9x isletim sistemlerinde dotNET programı
yazıp derleyemiyorsunuz, çünkü .NET programlarını yazabilmek için Microsoft 2 araç
sunuyor.
1 ) .NET Framework SDK: Yükleyebilmek için en az windows NT 4.0 SP6 kurulu 32 MB
RAM'li bir sistem gerekmektedir.
2 ) Visual Studio .NET: Yükleyebilmek icin en az Windows 2000 Professional SP3 ve 96
MB RAM içeren bir sistem gerekmektedir.
Diyelim ki elimizde bir Windows 98 isletim sistemi yüklü sistem var. Bu sistemin belleği
de sadece 32 MB olsun. Bu özelliklere sahip bir sistem üzerinde .NET programları yazmak
isteyelim. Bunu yapabilir miyiz?
Eğer .NET programlarını yazmanın tek yolu yukaridaki araçlari kullanmak olsaydı bu
sorunun yanıtı "hayır" olacaktı. Fakat .NET programlarını yazmanın birkaç yolu daha var.
Bunlardan bir tanesi Mono projesi dahilinde gelistirilen C# derleyicisidir. http://www.gomono.
com/ adresinden projeyle ilgili bilgilere ve gerekli tüm programlara ücretsiz
ulasabilir bilgisayarınıza indirebilirsiniz. mono'nun bugünku tarih itibariyle Windows icin
0.28 sürümü mevcut. Mono'nun CLR altyapısı ile basit bir uygulamayi "mint den1.exe"
seklinde çalıstırmak istediğimde benim bilgisayarımda "bellek yetersiz" gibi bir hata verdi.
Bu yüzden Microsoft .NET Framework kurmanızı da tavsiye ederim. Microsoft'un
sitesinden son .net framework kurulum dosyasını indirrip kurduktan sonra Mono'nun
"mcs" derleyicisi ile derlediginiz programları normal Windows uygulaması çalıstırıyormus
gibi çift tıklayarak çalıstırabilirsiniz. Asağıda basit bir örnek görülüyor.
class den1{
public static void Main(){
System.Console.WriteLine("denemedir.");
System.Console.ReadLine();
}
}
Bu örnek uygulamayı mcs derleyicisi ile asağıdaki gibi derliyoruz.
D:\Program Files\Mono-0.28\bin\>mcs den1.cs
Compilation succeeded
Olusan dosyaya Çift tıklayarak ya da exe'nin adını yazarak uygulamayı çalıstırıyoruz.
D:\Program Files\Mono-0.28\bin\>den1.exe
Denemedir.
Đsterseniz daha karmasık bir uygulama ile mcs derleyicisinin yeteneklerini test edelim.
Matematik'te satır, sütun veya diyagonallerindeki sayıların toplamının hep aynı sayıya esit
olduğu karelere "sihirli kare" denir. Verilen bir tek sayılı boyut icin sihirli kare olusturan
algoritma uygulaması C# ile verilmistir. Program Visual Studio .NET 2003 ortamında
yazılımıstır ve derlenmistir. VS.NET ile olusturulan calıstırılabilir dosyanın adı
WindowsApplication6.exe'dir. Aynı kaynak kod (Form1.cs) hicbir değisikliğe ugratılmadan
Windows 98 uzerine kurulu Mono-0.28 ve .NET Framework 1.1 yuklu makinede Mono
derleyicisiyle "mcs Form1.cs -r:System.Windows.Forms -r:System.Drawing -
r:System.Data" komutuyla derlenmistir. Derleme basarıyla sonuclanmıs ve Form1.exe
adli dosya olusmustur. Asağıdaki masaustu görüntüsunde sol tarafta çalıstırılan uygulama
Mono ile derlenen, sağ tarafta çalıstırılan uygulama ise (aynı kaynak koddan derlenmistir)
VS.NET 2003'te derlenmistir. Đki dosya da çift tıklanarak çalıstırılmıstır.
Sekil 1: Mono ve VS.NET ile derlenen uygulamalar.
VS.NET Uygulamasını indirmek için tıklayın.
Mono ile derlenen uygulamayı indirmek için tıklayın.
Sihirli Karelerin Olusturulması - Basamak Yöntemi
"Sihirli Kare" olusturmak icin kullanılan yöntem La Loubere'in bulduğu "Basamak" adı
verilen yontemdir.
Asağıda genel kuralı verilen "Basamak" yöntemi her tek boyutlu sihirli kareyi olusturabilir.
Asagidaki anlatım "Yasayan Matematik" adli kitabin 53. sayfasından alınmıstır, bu konu
hakkında daha detayli bilgi ve buna benzer keyifli matematik eğlencelerini öğrenmek için
bu kitaba basvurmanız tavsiye edilir. Bu yöntemin 3x3'lük bir sihirli kareye uygulanısı
asağıdaki sekilde gösterilmistir.
Sekil 2: Sihirli kare algoritması
"
1) 1 sayısını en üst satırın ortasındaki kareye yerlestirerek baslayalım.
2) Her koyduğumuz sayının sağ üst çaprazına bir sonraki sayıyı koyalım. Eğer burası
sihirli karenin dısındaki hayali bir kareyse (a,b,...,g diye isimlendirdiklerimizden biriyse)
sihirli karede bu yere denk gelen kutuyu bulup buraya sayımızı yerlestirelim.
3) Eğer sihirli karedeki sağ üst çapraz doluysa, o zaman sayıyı bir onceki sayının
altındaki kutuya yerlestirelim (4 ve 7 sayılarında olduğu gibi).
4) 2. ve 3. basamakları uygulamayı sürdurerek sihirli karedeki diğer sayıların yerlerini
bulalım."
["Yasayan Matematik",s.53]
Tavsiyeler
Mono'nun resmi sayfası
Mono semineri slaytları
Referans
"Yasayan Matematik", Theoni PAPPAS, Türkçe'ye çeviren: Yıldız SĐLĐER, Sarmal Yayımevi,
Ekim 1993.
Windows XP Stillerinin Kontrollere Uyarlanması
Merhaba, bu makalede Windows XP sitillerinin form kontrollerine herhangi bir Manifest
dosyası olmadan uygulanmasını tartısacağız. Ben uygulama örneği olarak bir ProgressBar
kontrolünü seçtim. Çalıstırılabilir örnek kod bu siteden indirilebilir.
Herseyden önce bu isteğimizi gerçeklestirmek için isteğimizi uygulayacağımız bir sınıfa
sahip olmamız gerekir. Ne yazık ki standart ProgressBar sınıfı “sealed” olduğu için ben
sınıfımı Control sınıfından türettim:
public class XPProgressBar:Control
{
}
Daha sonra yapmamız gereken standart ProgressBar özelliklerini (Maximum, Minimum,
Value, Step) özelliklerini sınıfımıza uygulamaktır. Bu özelliklerin hepsi integer’dir. Kolay
olduğu için bu kısmı geçiyorum. Dikkat edilmesi gereken nokta Value özelliğine bir değer
geçerken sınıf örneğimizin yeniden boyanmasını sağlamaktır. Bunu da Refresh()
fonksiyonu ile yapabiliriz. Uygulamamızın amacı XP sitillerinin uygulanması olduğu için
Style isminde bir özellik tanıtırız. Bu özellik Normal ve System isimlerinde iki enum değeri
tutar. Eğer özelliğimizin değeri System ise XP sitili uygulanır.
public Styles Style
{
get {<return FStyle;}
set
{
FStyle=value;
Refresh();
}
}
Simdi sınıfımızın boyanması için OnPaint metoduna geçebiliriz. Đlkönce Normal değerini
görelim.
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
rect.Width-=6;
rect.Height-=6;
if(Maximum!=Minimum)
{
rect.Width =(Value-Minimum)*100/(Maximum-Minimum)*rect.Width/100;
}
if(Style==Styles.Normal)
{
ControlPaint.DrawBorder3D(e.Graphics,ClientRectangle,Border3DStyle.Sunken);
rect=new Rectangle(new Point(rect.X+3,rect.Y+3),rect.Size);
e.Graphics.FillRectangle(Brushes.Blue,rect);
}
else
{
//Burada Styles.System değerini uygulayacağız.
}
}
Yukarıdaki kodda yapılan Maximum ve Minimum değerlerine göre boyanacak alanın
bulunup daha sonra boyanmasıdır. Dikkat edeceğiniz gibi segment seklinde değil de düz
boya seklinde boyanmıstır. Eğer isterseniz bu asamada bilesenimizi test edebilirsiniz.
Dikkat edeceğiniz gibi standart bir ProgressBar’ın yapacağı islemleri gerçeklestirdik. Simdi
asıl amacımız XP sitillerinin uygulanması olduğuna göre bu asamaya geçelim.
Öncelikle kullandığımız isletim sisteminin yapacağımız bu islemi desteklemesi için
Windows XP olması lazım. Bunu nasıl anlayacağımızı görelim:
bool IsThemedos()
{
if(Environment.OSVersion.Platform != PlatformID.Win32NT
|| Environment.OSVersion.Version.Major < 5
|| Environment.OSVersion.Version.Minor < 1)
return false;
return true;
}
Yukarıdaki fonksiyonda yapılan kullanılan isletim sisteminin version numaralarına bakarak
Windows XP olup almadığını anlamaktır. Eğer değilse program XP sitillerinin kullanımına
izin vermeyecektir.
Simdi burada bir ara verip XP sitillerinin kullanımına olanak veren WinApi leri tanıtmak
istiyorum. Öncelikle kullanacağımız dll “UxTheme.dll” dir. Bizim bu programda
kullanacağımız 4 tane WinApi fonksiyonu var. Simdi bunları tanıyalım:
HTHEME OpenThemeData( HWND hwnd,LPCWSTR pszClassList);
Bu fonksiyon bir window için ilgili sınıfın datasını açar. Dönüs değeri IntPtr dir.
“pszClassList” parametresi ise kullanacağımız sınıfın string değeridir. Bizim örneğimizde
bu değer "PROGRESS" dir.
BOOL IsAppThemed(VOID);
Bu Api bir kontrol fonksiyonudur. uygulamamızın visual sitilleri uygulayıp
uygulamayacağını sorgular.
BOOL IsThemeActive(VOID);
Bu da baska bir kontrol fonksiyonudur. Visual sitillerin uygulamamız için aktif olup
olmadığını denetler.
HRESULT DrawThemeBackground(
HTHEME hTheme,
HDC hdc,
int iPartId,
int iStateId,
const RECT *pRect,
const RECT *pClipRect
);
Uygulamamızın belki de en can alıcı Api si budur. Bu fonksiyonla visual sitillerin çizim
islemini gerçeklestiriyoruz. Dönüs değeri integer dir. Parametrelerine gelince:
hTheme: OpenThemeData fonksiyonu ile elde ettiğimiz IntPtr değerini kullanacağız.
hdc: Controlümüzün hdc değeri. CreateGraphics().GetHdc() ile elde edilir.
iPartId ve iStateId: Bu parametreler çizeceğimiz kontrolün bölümlerini ifade eder. Visual
Studio nun yardım indeksi bölümüne “Parts and States” yazarsak konuyla ilgili dökümanı
bulabiliriz. Bu durumda Controlümüzün arkaplanını çizeceksek iPartId 1 değerini, eğer
öndeki ilerleme bölümünü çizeceksek 3 değerini almalıdır. StateId değeri ise ProgressBar
için kullanılmaz. Eğer dökümanı iyice incelersek yapacağımız diğer uygulamalarda XP
sitillerini nasıl uygulayacağımız kolaylıkla anlasılır.
pRect ve pClipRect: Bu parametreler çizeceğimiz dörtgen bölümü ifade eder. ClipRect
“null” değerini alabilir.
Simdi programımıza geri dönüp anlattığımız Api lerin kullanımına geçebiliriz. Öncelikle
sınıfımızın yapıcısında OpenThemeData apisini kullanarak XP sitilimizin değerini
tutabilirz. Daha sonra sınıfımızın OnPaint metodunda bos bıraktığımız “else” ifadesini
dolduralım.
if(IsThemedos() && IsAppThemed() && IsThemeActive())
{
DrawThemeBackground(e.Graphics,1,1,ClientRectangle);
DrawSystemSegments(e.Graphics,rect);
}
private void DrawSystemSegments(Graphics g, Rectangle rc)
{
int segwidth = 8;
int Count = ((rc.Width)/(segwidth+2))+((rc.Width<=0)?0:1);
Rectangle rect = new Rectangle(3,3,Count*(segwidth+2),rc.Height);
if (rect.Width>(Width-2*3))
{
rect.Width = rc.Width;
}
DrawThemeBackground(g,3,1,rect);
}
Öncelikle kontrolümüzün arka planını çizdik. Daha sonra DrawSystemSegments
fonksiyonu ile segmentleri hesaplayıp sonra da bunu çizdirdik.
Bu konu hakkında anlatacaklarım bitti. Eğer örnek kod incelenirse anlasılmayan
bölümlerin anlasılmasına yardımcı olacaktır. Hepinize iyi çalısmalar.
Not : XP stilindeki kontrolleri görmeniz için isletim sisteminizin temasını XP Stil olarak
değistirmeniz gerekmektedir.
Hashtable Koleksiyon Sınıfının Kullanımı
Bu makalemizde HashTable koleksiyon sınıfını incelemeye çalısacağız. Bildiğiniz gibi
Koleksiyonlar System.Collections namespace'inde yer almakta olup, birbirlerinin aynı
veya birbirlerinden farklı veri tiplerinin bir arada tutulmasını sağlayan diziler
olusturmamıza imkan sağlamaktadırlar. Pek çok koleksiyon sınıfı vardır. Bugün bu
koleksiyon sınıflarından birisi olan HashTable koleksiyon sınıfını inceleyeceğiz.
HashTable koleksiyon sınıfında veriler key-value dediğimiz anahtar-değer çiftleri seklinde
tutulmaktadırlar. Tüm koleksiyon sınıflarının ortak özelliği barındırdıkları verileri object
tipinde olmalarıdır. Bu nedenle, HashTable'lardada key ve value değerleri herhangibir veri
tipinde olabilirler. Temel olarak bunların her biri birer DictionaryEntry nesnesidir.
Bahsetmis olduğumuz key-value çiftleri hash tablosu adı verilen bir tabloda saklanırlar.
Bu değer çiftlerine erismek için kullanılan bir takım karmasık kodlar vardır.
Key değerleri tektir ve değistirilemezler. Yani bir key-value çiftini koleksiyonumuza
eklediğimizde, bu değer çiftinin value değerini değistirebilirken, key değerini
değistiremeyiz. Ayrıca key değerleri benzersiz olduklarında tam anlamıyla birer anahtar
alan vazifesi görürler. Diğer yandan value değerline null değerler atayabilirken, anahtar
alan niteliğindeki Key değerlerine null değerler atayamayız. Sayet uygulamamızda
varolan bir Key değerini eklemek istersek ArgumentException istisnası ile karsılasırız.
HashTable koleksiyonu verilere hızı bir biçimde ulasmamızı sağlayan bir kodlama yapısına
sahiptir. Bu nedenle özellikle arama maliyetlerini düsürdüğü için tercih edilmektedir.
Simdi konuyu daha iyi pekistirebilmek amacıyla, hemen basit bir uygulama gelistirelim.
Uygulamamızda, bir HastTable koleksiyonuna key-value çiftleri ekliyecek, belirtilen key'in
sahip olduğu değere bakılacak, tüm HashTable'ın içerdiği key-value çiftleri listelenecek,
eleman çiftlerini HashTable'dan çıkartacak vb... islemler gerçeklestireceğiz. Form
tasarımını ben asağıdaki sekildeki gibi yaptım. Temel olarak teknik terimlerin türkçe
karsılığına dair minik bir sözüğü bir HashTable olarak tasarlayacağız.
Sekil 1. Form Tasarımımız.
Simdi kodlarımıza bir göz atalım.
System.Collections.Hashtable htTeknikSozluk; /* HashTable koleksiyon nesnemizi
tanimliyoruz.*/
private void Form1_Load(object sender, System.EventArgs e)
{
htTeknikSozluk=new System.Collections.Hashtable(); /* HashTable nesnemizi
olusturuyoruz.*/
stbDurum.Text=htTeknikSozluk.Count.ToString(); /* HashTable'imizdaki eleman
sayisini Count özelligi ile ögreniyoruz.*/
}
private void btnEkle_Click(object sender, System.EventArgs e)
{
try
{
htTeknikSozluk.Add(txtKey.Text,txtValue.Text);/* HashTable'imiza key-value çifti
ekleyebilmek için Add metodu kullaniliyor.*/
lstAnahtar.Items.Add(txtKey.Text);
stbDurum.Text=htTeknikSozluk.Count.ToString();
}
catch(System.ArgumentException) /* Eger var olan bir key'i tekrar eklemeye çalisirsak
bu durumda ArgumentException istisnasi firlatilacaktir. Bu durumda, belirtilen keyvalue
çifti HashTable koleksiyonuna eklenmez. Bu durumu kullaniciya bildiriyoruz.*/
{
stbDurum.Text=txtKey.Text+" Zaten HashTable Koleksiyonunda Mevcut!";
}
}
private void lstAnahtar_DoubleClick(object sender, System.EventArgs e)
{
string deger;
deger=htTeknikSozluk[lstAnahtar.SelectedItem.ToString()].ToString(); /*
HashTable'daki bir degere ulasmak için, köseli parantezler arasinda aranacak key
degerini giriyoruz. Sonucu bir string degiskenine aktariyoruz.*/
MessageBox.Show(deger,lstAnahtar.SelectedItem.ToString());
}
private void btnSil_Click(object sender, System.EventArgs e)
{
if(htTeknikSozluk.Count==0)
{
stbDurum.Text="Çikartilabilecek hiç bir eleman yok";
}
else if(lstAnahtar.SelectedIndex==-1)
{
stbDurum.Text="Listeden bir eleman seçmelisiniz";
}
else
{
htTeknikSozluk.Remove(lstAnahtar.SelectedItem.ToString()); /* Bir HashTable'dan
bir nesneyi çikartmak için, Remove metodu kullanilir. Bu metod parametre olarak
çikartilmak istenen deger çiftinin key degerini alir.*/
lstAnahtar.Items.Remove(lstAnahtar.SelectedItem);
stbDurum.Text="Çikartildi";
stbDurum.Text=htTeknikSozluk.Count.ToString();
}
}
private void btnTumu_Click(object sender, System.EventArgs e)
{
lstTumListe.Items.Clear(); /* Asagidaki satirlarda, bir HashTable koleksiyonu içinde
yer alan tüm elemanlara nasil erisildigini görmekteyiz. Keys metodu ile HashTable
koleksiyonumuzda yer alan tüm anahtar degerlerini (key'leri), ICollection
arayüzü(interface) türünden bir nesneye atiyoruz. Foreach döngümüz ile bu nesne
içindeki her bir anahtari, HashTable koleksiyonunda bulabiliyoruz.*/
ICollection anahtar=htTeknikSozluk.Keys; foreach(string a in anahtar)
{
lstTumListe.Items.Add(a+"="+htTeknikSozluk[a].ToString());
}
}
Simdi uygulamamızı çalıstıralım.
Sekil 2. Programın Çalısmasnının sonucu.
Geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüsmek dieğiyle
hepinize mutlu günler dilerim.
Stack ve Queue Koleksiyon Sınıfları
Bu makalemizde Stack ve Queue koleksiyon sınıflarını incelemeye çalısacağız. Bir önceki
makalemizde bildiğiniz gibi, HashTable koleksiyon sınıfını incelemestik. Stack ve Queue
koleksiyonlarıda, System.Collections isim alanında yer alan ve ortak koleksiyon
özelliklerine sahip sınıflardır.
Stack ve Queue koleksiyonları, her koleksiyon sınıfında olduğu gibi, elemanlarını object
tipinde tutmaktadırlar. Bu koleksiyonların özelliği giren-çıkan eleman prensibleri üzerine
çaılsmalarıdır. Stack koleksiyon sınıfi, LIFO adı verilen, Last In First Out( Son giren ilk
çikar) prensibine gore çalısırken, Queue koleksiyon sınıfı FIFO yani First In First Out(ilk
giren ilk çıkar) prensibine gore çalısır. Konuyu daha iyi anlayabilmek için asağıdaki
sekilleri göz önüne alalım.
Sekil 1. Stack Koleksiyon Sınıfının Çalısma Yapısı
Görüldüğü gibi, Stack koleksiyonunda yer alan elemanlardan son girene ulasmak oldukça
kolaydır. Oysaki ilk girdiğimiz elemana ulasmak için, bu elemanın üstünde yer alan diğer
tüm elemanları silmemiz gerekmektedir. Queue koleksyion sınıfına gelince;
Sekil 2. Queue Koleksiyon Sınıfının Çalısma Yapısı
Görüldügü gibi Queue koleksiyon sinifinda elemanlar koleksiyona arkadan katilirlar ve ilk
giren eleman kuyruktan ilk çikan eleman olur.
Stack ve Queue farklı yapılarda tasarlandıkları için elemanlarına farklı metodlar ile
ulasılmaktadır. Stack koleksiyon sınıfında, en son giren elemanı elde etmek için Pop
metodu kullanılır. Koleksiyona bir eleman eklerken Push metodu kullanılır. Elbette
eklenen eleman en son elemandır ve Pop metodu çağrıldığında elde edilecek olan ilk
eleman halini alir. Ancak Pop metodu son giren elemanı verirken bu elemanı
koleksiyondan siler. Đste bunun önüne geçen metod Peek metodudur. Simdi diyebilirsiniz
ki madem son giren elemanı siliniyor, Pop metodunu o zaman niye kullanıyoruz.
Hatırlarsanız, Stack koleksiyonunda, ilk giren elemanı elde etmek için bu elemanın
üstünde yer alan tüm elemanları silmemiz gerektiğini söylemistik. Đste bir döngü
yapısında Pop metodu kullanıldığında, ilk giren elemana kadar inebiliriz. Tabi diğer
elemanlari kaybettikten sonra bunun çok büyük önem tasıyan bir eleman olmasını da
istemis olabiliriz.
Gelelim Queue koleksiyon sınıfının metodlarına. Dequeue metodu ile koleksiyona ilk giren
elemani elde ederiz. Ve bunu yaptığımız anda eleman silinir. Nitekim dequeue metodu
pop metodu gibi çalısır. Koleksiyona eleman eklemek için ise, Enqueue metodu kullanılır.
Đlk giren elemanı elde etmek ve silinmemesini sağlamak istiyorsak yine stack koleksiyon
sınıfinda olduğu gibi, Peek metodunu kullanırız.
Bu koleksiyonlarin en güzel yanlarından birisi siz eleman sayısını belirtmediğiniz takdirde
koleksiyonun boyutunu otomatik olarak kendilerinin ayarlamalarıdır. Stack koleksiyon
sınıfı, varsayilan olarak 10 elemanlı bir koleksiyon dizisi olusturur.(Eğer biz eleman
sayısını yapıcı metodumuzda belirtmez isek).Eğer eleman sayısı 10’u geçerse, koleksiyon
dizisinin boyutu otomatik olarak iki katina çıkar. Ayni prensip queue koleksiyon sinifi
içinde geçerli olmakla birlikte, queue koleksiyonu için varsayilan dizi boyutu 32 elemanli
bir dizidir.
Simdi dilerseniz, basit bir console uygulamasi ile bu konuyu anlamaya çalisalim.
using System;
using System.Collections; /* Uygulamalarimizda koleksiyon siniflarini kullanabilmek için
Collections isim uzayini kullanmamiz gerekir.*/
namespace StackSample1
{
class Class1
{
static void Main(string[] args)
{
Stack stc=new Stack(4); /* 4 elemanli bir Stack koleksiyonu olusturduk.*/
stc.Push("Burak"); /*Eleman eklemek için Push metodu kullaniliyor.*/
stc.Push("Selim");
stc.Push("SENYURT");
stc.Push(27);
stc.Push(true);
Console.WriteLine("Çikan eleman {0}",stc.Pop().ToString());/* Pop metodu
son giren(kalan) elemani verirken, ayni zamanda bu elemani koleksiyon dizisinden
siler.*/
Console.WriteLine("Çikan eleman {0}",stc.Pop().ToString());
Console.WriteLine("Çikan eleman {0}",stc.Pop().ToString());
Console.WriteLine("------------------");
IEnumerator dizi=stc.GetEnumerator(); /* Koleksiyonin elemanlarini
IEnumerator arayüzünden bir nesneye aktariyoruz.*/
while(dizi.MoveNext()) /* dizi nesnesinde okunacak bir sonraki eleman var
oldugu sürece isleyecek bir döngü.*/
{
Console.WriteLine("Güncel eleman {0}",dizi.Current.ToString()); /*
Current metodu ile dizi nesnesinde yer alan güncel elemani elde ediyoruzç. Bu döngüyü
çalistirdigimizda sadece iki elemanin dizide oldugunu görürüz. Pop metodu sagolsun.*/
}
Console.WriteLine("------------------");
Console.WriteLine("En üstteki eleman {0}",stc.Peek()); /* Peek metodu son
giren elemani veya en üste kalan elemani verirken bu elemani koleksiyondan silmez.*/
dizi=stc.GetEnumerator();
while(dizi.MoveNext())
{
Console.WriteLine("Güncel eleman {0}",dizi.Current.ToString()); /* Bu
durumda yine iki eleman verildigini Peek metodu ile elde edilen elemanin koleksiyondan
silinmedigini görürüz.*/
}
}
}
}
3. Stack ile ilgili programın ekran çıktısı
Queue örnegimiz ise aynı kodlardan olusuyor sadece metod isimleri farklı.
using System;
using System.Collections;
namespace QueueSample1
{
class Class1
{
static void Main(string[] args)
{
Queue qu=new Queue(4);
qu.Enqueue("Burak"); /*Eleman eklemek için Enqueue metodu kullaniliyor.*/
qu.Enqueue("Selim");
qu.Enqueue("SENYURT");
qu.Enqueue(27);
qu.Enqueue(true);
Console.WriteLine("Çikan eleman {0}",qu.Dequeue().ToString());/* Dequeue
metodu ilk giren(en alttaki) elemani verirken, ayni zamanda bu elemani koleksiyon
dizisinden siler.*/
Console.WriteLine("Çikan eleman {0}",qu.Dequeue().ToString());
Console.WriteLine("Çikan eleman {0}",qu.Dequeue().ToString());
Console.WriteLine("------------------");
IEnumerator dizi=qu.GetEnumerator(); /* Koleksiyonin elemanlarini
IEnumerator arayüzünden bir nesneye aktariyoruz.*/
while(dizi.MoveNext()) /* dizi nesnesinde okunacak bir sonraki eleman var
oldugu sürece isleyecek bir döngü.*/
{
Console.WriteLine("Güncel eleman {0}",dizi.Current.ToString()); /* Current
metodu ile dizi nesnesinde yer alan güncel elemani elde ediyoruzç. Bu döngüyü
çalistirdigimizda sadece iki elemanin dizide oldugunu görürüz. Dequeue metodu
sagolsun.*/
}
Console.WriteLine("------------------");
Console.WriteLine("En altta kalan eleman {0}",qu.Peek()); /* Peek metodu
son giren elemani veya en üste kalan elemani verirken bu elemani koleksiyondan
silmez.*/
dizi=qu.GetEnumerator();
while(dizi.MoveNext())
{
Console.WriteLine("Güncel eleman {0}",dizi.Current.ToString()); /* Bu
durumda yine iki eleman verildigini Peek metodu ile elde edilen elemanin koleksiyondan
silinmedigini görürüz.*/
}
}
}
}
Sekil 4. Queue ile ilgili programin ekran çıktısı
Geldik bir makalemizin daha sonuna. Umuyorumki sizlere faydalı olabilecek bilgiler
sunabilmisimdir. Bir sonraki makalemizde görüsmek dileğiyle hepinize mutlu günler
dilerim.
Reflection(Yansıma) ile Tiplerin Sırrı Ortaya Çıkıyor
Hiç dotNET ‘te yer alan bir tipin üyelerini öğrenebilmek istediniz mi? Örneğin var olan bir
dotNET sınıfının veya sizin kendi yazmıs olduğunuz yada bir baskasının yazdığı sınıfa ait
tüm üyelerin neler olduğuna programatik olarak bakmak istediniz mi?
Đste bugünkü makalemizin konusu bu. Herhangi bir tipe (type) ait üyelerin neler
olduğunu anlayabilmek. Bu amaçla, Reflection isim uzayını ve bu uzaya ait sınıfları
kullanacağız. Bildiğiniz gibi .NET ‘te kullanılan her sey bir tipe aittir. Yani herseyin bir tipi
vardır. Üyelerini öğrenmek isteğimiz bir tipi öncelikle bir Type değiskeni olarak alırız.
(Yani tipin tipini alırız. Bu nedenle ben bu tekniğe Tip-i-Tip adını verdim ). Bu noktadan
sonra Reflection uzayına ait sınıfları ve metodlarını kullanarak ilgili tipe ait tüm bilgileri
edinebiliriz. Küçük bir Console uygulaması ile konuyu daha iyi anlamaya çalısalım. Bu
örneğimizde, System.Int32 sınıfına ait üyelerin bir listesini alacağız. Đste kodlarımız;
using System;
namespace ReflectionSample1
{
class Class1
{
static void Main(string[] args)
{
Type tipimiz=Type.GetType("System.Int32");/* Öncelikle String sinifinin tipini
ögreniyoruz. */
System.Reflection.MemberInfo[] tipUyeleri=tipimiz.GetMembers(); /* Bu satir
ile, System.String tipi içinde yer alana üyelerin listesini Reflection uzayinda yer alan,
MemberInfo sinifi tipinden bir diziye aktariyoruz. */
Console.WriteLine(tipimiz.Name.ToString()+" sinifindaki üye
sayisi:"+tipUyeleri.Length.ToString());/* Length özelligi, MemeberInfo tipindeki dizimizde
yer alan üyelerin sayisini, (dolayisiyla System.String sinifi içinde yer alan
üyelerin sayisini) veriyor.*/
/* Izleyen döngü ile, MemberInfo dizininde yer alan üyelerin birtakim bilgilerini
ekrana yaziyoruz.*/
for(int i=0;i<TIPUYELERI.LENGTH;++I)
{
Console.WriteLine(i.ToString()+". üye
adi:"+tipUyeleri[i].Name.ToString()+"||"+tipUyeleri[i].MemberType.ToString()); /*
Name özelligi üyenin adini verirken, MemberType özelligi ile, üyenin tipini aliyoruz. Bu
üye tipi metod, özellik, yapici metod vb... dir.*/
}
}
}
}
Uygulamayı çalıstırdığımızda asağıdaki ekran görüntüsünü elde ederiz.
Sekil 1. System.Int32 sınıfının üyeleri.
Üye listesini incelediğimizde 15 üyenin olduğunu görürüz. Metodlar, alanlar vardır. Ayrıca
dikkat ederseniz, Parse , ToString metodları birden fazla defa geçmektedir. Bunun nedeni
bu metodların overload ( asırı yüklenmis ) versiyonlara sahip olmasıdır.
Kodları incelediğimizde, System.Int32 sınıfına ait tipleri GetMembers metodu ile,
System.Reflection uzayında yer alan MemberInfo sınıfı tipinden bir diziye aldığımızı
görürüz. Đste olayın önemli kodları bunlardan olusmaktadır. MemberInfo dısında
kullanabaileceğimiz baska Reflection sınıflarıda vardır. Bunlar;
ConstructorInfo Tipe ait yapıcı metod üyelerini ve bu üyelere ait bilgilerini içerir.
EventInfo Tipe ait olayları ve bu olaylara ait bilgileri içerir.
MethodInfo Tipe ait metodları ve bu metodlara ait bilgileri içerir.
FieldInfo Tip içinde kullanılan alanları ve bu alanlara iliskin bilgileri içerir.
ParameterInfo Tip içinde kullanılan parametreleri ve bu parametrelere ait bilgileri
içerir.
PropertyInfo Tip içinde kullanılan özellikleri ve bu özelliklere ait bilgileri içerir.
Tablo 1. Reflection Uzayının Diğer Kullanıslı Sınıfları
Simdi bu sınıflara ait örneklerimizi inceleyelim.
Bu kez bir DataTable sınıfının üyelerini inceleyeceğiz. Örnek olarak, sadece olaylarını ve
bu olaylara iliskin özelliklerini elde etmeye çalısalım. Bu örneğimizde, yukarıda
bahsettiğimiz tip-i-tip tekniğini biraz değistireceğiz. Nitekim bu tekniği uygulamamız
halinde bir hata mesajı alırız. Bunun önüne geçmek için, bir DataTable örneği (instance)
olusturup bu örneğin tipinden hareket edeceğiz. Dilerseniz hemen kodlarımıza geçelim.
using System;
namespace ReflectionSample2
{
class Class1
{
static void Main(string[] args)
{
System.Data.DataTable dt=new System.Data.DataTable(); /* Bir DataTable
örnegi(instance) yaratiyoruz.*/
Type tipimiz=dt.GetType(); /* DataTable örnegimizin GetType metodunu
kullanarak, bu örnegin dolayisiyla DataTable sinifinin tipini elde ediyoruz. */
System.Reflection.MethodInfo[] tipMetodlari=tipimiz.GetMethods(); /* Bu kez
sadece metodlari incelemek istedigimizden, GetMethods metodunu kullaniyor ve
sonuçlari, MethodInfo sinifi tipinden bir diziye aktariyoruz.*/
Console.WriteLine(tipimiz.Name.ToString()+" sinifindaki metod
sayisi:"+tipMetodlari.Length.ToString()); /* Metod sayisini Length özelligi ile aliyoruz.*/
/* Döngümüzü olusturuyor ve Metodlarimizi bir takim özellikleri ile birlikte
yazdiriyoruz.*/
for(int i=0;i<TIPMETODLARI.LENGTH;++I)
{
Console.WriteLine("Metod adi:"+tipMetodlari[i].Name.ToString()+" |Dönüs
degeri:"+tipMetodlari[i].ReturnType.ToString()); /* Metodun ismini name özelligi ile
aliyoruz. Metodun dönüs tipini ReturnType özelligi ile aliyoruz. */
System.Reflection.ParameterInfo[]
prmInfo=tipMetodlari[i].GetParameters();
/* Bu satirda ise, i indeksli metoda ait parametre bilgilerini GetParameters
metodu ile aliyor ve Reflection uzayinda bulunan ParameterInfo sinifi tipinden bir diziye
aktariyoruz. Böylece ilgili metodun parametrelerine ve parametre bilgilerine
erisebilicez.*/
Console.WriteLine("-----Parametre Bilgileri-----
"+prmInfo.Length.ToString()+" parametre");
/* Döngümüz ile i indeksli metoda ait parametrelerin isimlerini ve tiplerini
yazdiriyoruz. Bunun için Name ve ParameterType metodlarini kullaniyoruz.*/
for(int j=0;j<PRMINFO.LENGTH;++J)
{
Console.WriteLine("P.Adi:"+prmInfo[j].Name.ToString()+"
|P.Tipi:"+prmInfo[j].ParameterType.ToString());
}
Console.WriteLine("----");
}
}
}
}
Simdi uygulamamızı çalıstıralım ve sonuçlarına bakalım.
Sekil 2. System.Data.DataTable tipinin metodları ve metod parametrelerine ait bilgiler.
Reflection teknikleri yardımıyla çalıstırdığımız programa ait sınıflarında bilgilerini elde
edebiliriz. Đzleyen örneğimizde, yazdığımız bir sınıfa ait üye bilgilerine bakacağız.
Üyelerine bakacağımız sınıfın kodları;
using System;
namespace ReflectionSample3
{
public class OrnekSinif
{
private int deger;
public int Deger
{
get
{
return deger;
}
set
{
deger=value;
}
}
public string metod(string a)
{
return "Burak Selim SENYURT";
}
int yas=27;
string dogum="istanbul";
}
}
ikinci sınıfımızın kodları;
using System;
using System.Reflection;
namespace ReflectionSample3
{
class Class1
{
static void Main(string[] args)
{
Type tipimiz=Type.GetType("ReflectionSample3.OrnekSinif");
MemberInfo[] tipUyeleri=tipimiz.GetMembers();
for(int i=0;i<TIPUYELERI.LENGTH;++I)
{
Console.WriteLine("Uye adi:"+tipUyeleri[i].Name.ToString()+" |Uye
Tipi:"+tipUyeleri[i].MemberType.ToString());
}
}
}
}
Simdi uygulamamızı çalıstıralım.
Sekil 3. Kendi yazdığımı sınıf üyelerinede bakabiliriz.
Peki kendi sınıfımıza ait bilgileri edinmenin bize ne gibi bir faydası olabilir. Đste simdi tam
disimize gore bir örnek yazacağız. Örneğimizde, bir tablodaki verileri bir sınıf içersinden
tanımladığımız özelliklere alacağız. Bu uygulamamız sayesinde sadece tek satırlık bir kod
ile, herhangibir kontrolü veriler ile doldurabileceğiz. Bu uygulamada esas olarak, veriler
veritabanındaki tablodan alınıcak ve olusturduğumuz bir koleksiyon sınıfından bir diziye
aktarılacak. Olusturulan bu koleksiyon dizisi, bir DataGrid kontrolü ile iliskilendirilecek.
Teknik olarak kodumuzda, Reflection uzayının PropertyInfo sınıfını kullanarak,
olusturduğumuz sınıfa ait özellikler ile tablodaki alanları karsılastıracak ve uygun iseler bu
özelliklere tabloda karsılık gelen alanlar içindeki değerleri aktaracağız. Dilerseniz
kodumuz yazmaya baslayalım.
using System;
using System.Reflection;
using System.Data;
using System.Data.SqlClient;
namespace ReflectDoldur
{
public class Kitap
{
private string kitapAdi;
private string yayimci;
/* Özelliklerimizin adlarinin tablmuzdan alacagimiz alan adlari ile ayni olmasina
dikkat edelim.*/
public string Adi
{
get
{
return kitapAdi;
}
set
{
kitapAdi=value;
}
}
public string BasimEvi
{
get
{
return yayimci;
}
set
{
yayimci=value;
}
}
/* Yapici metodumuz parametre olarak geçirilen bir DataRow degiskenine sahip. Bu
degisken ile o anki satiri aliyoruz.*/
public Kitap(System.Data.DataRow dr)
{
PropertyInfo[] propInfos=this.GetType().GetProperties(); /* this ile bu sinifi
temsil ediyoruz. Bu sinifin tipini alip bu tipe ait özellikleri elde ediyor ve bunlar bir
PropertyInfo sinifi tipinden diziye aktariyoruz.*/
/* Döngümüz ile tüm özellikleri geziyoruz. Eger metodumuza parametre olarak
geçirilen dataRow degiskenimiz, bu özelligin adinda bir alan içeriyorsa, bu özellige ait
SetValue metodunu kullanarak özelligimize, iligili tablo alaninin degerini aktariyoruz.*/
for(int i=0;i<PROPINFOS.LENGTH;++I)
{
if(propInfos[i].CanWrite) /* Burada özelligimizin bir Set bloguna sahip olup
olmadigina bakiliyor. Yani özelligimizen yazilabilir olup olmadigina. Bunu saglayan
özelligimiz CanWrite. Eger özellik yazilabilir ise (yada baska bir deyisle readonly degil ise)
true degerini döndürür.*/
{
try
{
if(dr[propInfos[i].Name]!=null) /* dataRow degiskeninde, özelligimin
adindaki alanin degerine bakiliyor. Null deger degil ise SetValue ile alanin degeri
özelligimize yazdiriliyor. */
{
propInfos[i].SetValue(this,dr[propInfos[i].Name],null);
}
else
{
propInfos[i].SetValue(this,null,null);
}
}
catch
{
propInfos[i].SetValue(this,null,null);
}
}
}
}
}
/* KitapKoleksiyonu sinifimiz bir koleksiyon olucak ve tablomuzdan alacagimiz iki
alana ait verileri Kitap isimli nesnelerde saklanmasini sagliyacak. Bu nedenle, bir
CollectionBase sinifindan türetildi. Böylece sinifimiz içinde indeksleyiciler
kullanabilecegiz.*/
public class KitapKoleksiyonu:System.Collections.CollectionBase
{
public KitapKoleksiyonu()
{
SqlConnection conFriends=new SqlConnection("data source=localhost;initial
catalog=Friends;integrated security=sspi");
SqlDataAdapter da=new SqlDataAdapter("Select Adi,BasimEvi From
Kitaplar",conFriends);
DataTable dtKitap=new DataTable();
da.Fill(dtKitap);
foreach(DataRow drow in dtKitap.Rows)
{
this.InnerList.Add(new Kitap(drow));
}
}
public virtual void Add(Kitap _kitap)
{
this.List.Add(_kitap);
}
public virtual Kitap this[int Index]
{
get
{
return (Kitap)this.List[Index];
}
}
}
}
Ve iste formumuzda kullandığımız tek satırlık kod;
private void btnDoldur_Click(object sender, System.EventArgs e)
{
dataGrid1.DataSource = new ReflectDoldur.KitapKoleksiyonu();
}
Simdi uygulamamızı çalıstıralım ve bakalım.
Sekil 4. Programın Çalısmasının Sonucu
Görüldüğü gibi tablomuzdaki iki Alana ait veriler yazdığımız KitapKoleksiyonu sınıfı
yardımıyla, her biri Kitap tipinde bir nesne alan koleksyionumuza eklenmis ve bu
sonuçlarda dataGrid kontrolümüze bağlanmıstır. Siz bu örneği dahada iyi bir sekilde
gelistirebilirisiniz. Umuyorumki bu örnekte yapmak istediğimizi anlamıssınızdır. Yansıma
tekniğini bu kod içinde kısa bir yerde kullandık. Sınıfın özelliklerinin isminin, tablodaki
alanların ismi ile aynı olup olmadığını ve aynı iseler yazılabilir olup olmadıklarını
öğrenmekte kullandık.
Değerli Okurlarım. Geldik bir makalemizin daha sonuna. Hepinize mutlu günler dilerim.
C# Komut Satırı Derleyicisi(csc.exe) ve Parametreleri
Bu yazıda sizlere önemli bir referans kaynağı olacağını düsündüğüm C# komut satırı
derleyicisinin özelliklerini ve parametrelerini bir arada inceleyeceğiz. Böyle bir kaynağı
olusturmamdaki sebep C# komut derleyiciisnin kullanımı ile ilgili bana gelen onlarca epostaya
toplu olarak cevap verebilmek.
Bildiğiniz üzere .NET ortamında etkili bir sekilde gelistirme yapabilmek için Visual
Studio.NET aracına ihtiyaç duyuyoruz. Ancak bu gelistirme ortamı olmadan da her tür
.NET uygulamasını gelistirme imkanına sahibiz. Bu imkanı sağlayan en önemli araç
elbetteki C#'ın komut satırından da çalısan csc.exe isimli derleyicisidir. C# komut satırı
derleyicisi .NET Framework SDK ile birlikte ücretsiz olarak dağıtılmaktadır. Dolayısıyla
.NET ortamında uygulama gelistirmek için yapmamız gereken tek sey www.microsoft.com
sitesinden .NET Farmework SDK'nın son sürümünü bilgisayarınıza indirmek ve kurmaktır.
C# derleyicisi komut satırından basitçe kullanılabilmektedir. C# derleyicisini komut
satırından en etkili bir sekilde kullanabilmek için isletim sisteminizde bir takım ayarlar
yapmanız gerekmektedir. Örneğin siz komut satırında herhangi bir dizin içerisindeyken
bile csc.exe çalıstırılabilir dosyasını çalıstırabilmeniz için csc.exe dosyasının bulunduğu
dizini sistem özelliklerinde tanımlamanız gerekmektedir. Bu islemi yapmak için kontrol
panelden system ikonunu ve ardından advanced sekmesini asağıdaki gibi seçin.
Advanced sekmesine geçtikten sonra yukarıdaki sekilde kırmızı ile isaretlenen
"Environment Variables" düğmesine tıklayın.(Kullandığınız isletim sistemine göre bu
butonun yeri değisik olabilir. Yukarıdaki ekran görüntüsü Windows XP isletim sistemine
aittir.) Karsınıza çıkacak olan asağıdaki ekrandan "System Variables" alanından Path
seçeneğini seçin.
Path seçecenği tıklanarak açılan penceredeki Variable value alanının sonuna ; karakterini
ekledikten sonra csc.exe dosyasının bulunduğu dizini yazın. csc.exe dosyası kullandığınız
isletim sistemini göre değisiklik gösterebilir ancak genellikle asağıdaki gibidir.
C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322
Son dizin kullandığınız Framework'un versiyon numarasını göstermektedir.
Yukarıdaki islemlerin doğru bir sekilde tamamlandığını kontrol etmek için komut satırını
açın ve "csc" yazın. Eğer asağıdaki hata ile karsılasıyorsanız islemleriniz basarı ile
gerçeklesmis demektir.
Bu hatanın sebebi csc.exe programına derlenecek dosyayı parametre olarak
vermememizdir.
Derleme Đslemi
csc.exe ile en basit derleme islemi bir girdi dosya ve çıktı dosya belirtme ile yapılır.
Örneğin kaynakkod.cs dosyasını Program.exe seklinde derlemek için asağıdaki komutu
çalıstırmamız yeterlidir.
> csc kaynakkod.cs /out:Program.exe
Eğer out parametresini kullanmayıp komutu
> csc kaynakkod.cs
seklinde çalıstırsaydık derleme islemi basarılı olurdu ancak olusturulan çalıstırılabilir
dosyanın adı kaynakkod.exe olurdu.
Proje Tipleri ve target parametresi
.NET ortamında birden fazla proje tipi vardır ve dolayısıyla her bir proje tipinin derleme
biçimi farklıdır. Örneğin bir önceki komutumuz derlem islemini bir konsol uygulamasına
göre yapacaktır. Esasında csc.exe derleyicisnin varsayılan derleme biçimide budur. Eğer
derleme islemini farklı uygulama tipleri için yapacak olursak derseyicinin target
parametresini kullanmamız gerekir. Örneğin kaynak kodumuzu bir windows uygulaması
olacak sekilde derlemek istiyorsak derleme komutu asağıdaki gibi olmalıdır.
> csc kaynakkod.cs /target:winexe /out:Program.exe
yada
> csc kaynakkod.cs /t:winexe /out:Program.exe
Eğer kaynak kodumuzu çalıstırılabilir bir uygulama yerine bir kütüphane dosyası olacak
sekilde derlemek istiyorsak asağıdaki komutu kullanmalıyız.
> csc kaynakkod.cs /target:library /out:Program.exe
Diğer bir derleme biçimi ise modül derlemesidir. Modüller içinde manifest dediğimiz
metadataları olmayan yalnızca kod bilgilerini içeren dosyalardır. Modüller çalıstırılabilir
değildir. Dolayısıyla modüller ancak manifest bilgisi olan baska bir derlenmis kütüphaneye
eklenmek için kullanılabilir. Modül seklind derleme için asağıdaki kmut kullanılmalıdır.
> csc kaynakkod.cs /target:module /out:Program.exe
Referans Bilgileri ve Response Dosyaları
csc.exe derleyicisi derleme islemini basarı ile gerçeklestriebilmesi için bazı kütüphanelere
ihtiyaç duyar. Bu kütüphaneler assembly dosyaları içinde barındırılmıstır. Bu
kütüphanalerin projemizde kullanıldığını belirtmek için reference parametresi kullanılır.
Eğer referans vermeniz gereken assembly dosyaları fazla ise bu isi otomatiklestirmek için
response. dosyaları kullanılır. response dosyasının yerine belirtmek için @ karakteri
kullanılır. Örnek bir derleme biçimi asağıdaki gibidir.
csc @response_dosyası /out:Program.exe kaynakdosya.cs
Derleyici Parametrelerine Toplu Bakıs
Asağıdaki listede csc derleyicisi ile kullanılabilecek bütün parametrelerin kullanımı ve
açıklaması verilmistir.
Parametre Kullanımı
/doc:dosya_ismi
Kaynak dosyasındaki XML yorumlarını ayrıstırarak farklı bir
dosyaya kaydetmek için kullanılan bir parametredir.
Hatırlayacağınız üzere C# ile yazılmıs kaynak kodda ///
karakterlerinden sonra XML formatında yorum
yazılabilmektedir.
/nooutput
Kaynak kodun derlenmesini sağlar ancak herhangi bir
çalıstırılabilir dosya olusturmaz. Bu parametre daha çok
kaynak kodda hata ayıklama için kullanılmaktadır.
/optimize
/optimize+
/optimize-
Derleme islemi sırasında kaynak kodda herhangi bir
optimizasyonun yapılıp yapılmayacağını belirten bir
parametredir. optimize ile optimize+ parametresi
esdeğerdedir.
/addmodule:modül_dosyası
Daha önce /module parametresi ile olusturulan modüllerin
herhangi bir çalıstırılabilir dosyaya eklenmesi amacıyla
kullanılır. Birden fazla modül dosyasını eklemek için ;
karakteri ile modül dosyalarını ayırmak gerekir.
/nostdlib
/nostdlib+
/nostdlib-
.NET'in standart kütüphanesi olan System.dll'in otomatik
olarak derlenecek koda eklenip eklenmemesini belirten
parametredir. Eğer System.dll'i kendi kaynak dosyamızda
kullanmayacaksak burdaki sınıfları kendimiz olusturmalıyız.
Çook nadir kullanılabilecek bir parametredir. /nostdlib ile
/nostdlib+ parametresi esdeğerdedir.
/reference:assembly_adi
yada
/r:assembly_adi
Baska bir assembly dosyasına ait mata verilere referans
vermek için kullanılan parametredir. Assembly'nin
bulunduğu yer göreceli adres olabileceği gibi tam adres de
olabilir. Eğer birden fazla referans dosyası belirtilecekse ;
karakteri ile ayrılmalıdır.
/define:SEMBOL
/d:SEMBOL
Derleme zamanında önislemci sembolü olusturmak için
kullanılır. Kaynak kod içerisinden yapılan #define ön islemci
komutuna karsılık gelmektedir.
/warn:<0,1,2,3,4>
/w:<0,1,2,3,4>
Derleme islemi sırasında verilecek uyarıların derecesini
belirlemek için kullanılan parametredir. Eğer bütün
uyarıların gösterilmesini istiyorsak /warn:4 seklinde
kullanmalıyız. /warn:0 parametresi ise hiçbir uyarının
görüntülenmemesini sağlar. 0 ile 4 arasındaki değerler ise
farklı tipteki uyarıların gösterilip gösterilmemesini sağlar.
/warnaserror
/warnaserror+
/warnaserror-
Derleme sırasındaki uyarıların hata gibi islenmesini sağlar.
Bu genellikle idealist programcıların kullandığı bir
parametredir. Eğer uyarı verecek bir durum varsa kodun
derlenmemesi sağlanır. /warnaserror ile /warnaserror+
esdeğerdedir.
/nowarn:uyarı_numarası
Belirtilen numaralı uyarının derleme sırasında verilmemesi
için bu parametre kullanılır. Eğer birden fazla uyarının
verilmemesini istiyorsak uyarı numaralarını ; karakteril ile
ayırmamız gerekmektedir.
/fullpaths
Derleme sonrasında eğer herhangi bir dosyada hata var ise
hatanın olustuğu dosyanın tam adresinin hata ile
belirtilmesini sağlayan parametredir.
/debug
/debug+
/debug-
Hata ayıklamada kullanılacak dosyaların olusturulması için
kullanılan pjarametredir. Eğer debugging islemini aktif hale
getirmek istiyorsak bu parametreyi kullanmamız gerekir.
/debug ve /debug+ parametreleri esdeğerdedir. Hata
ayıklama islemi varsayılan olarak aktif durumda değildir.
/debug parametresinin ayrıca full ve pdbonly seklinde iki
seçeneği vardır. Eğer full seçeceği /debug:full seklinde
yazılırsa hata ayıklacı programı çalıstırılan program ile
iliskilendirilir.
/checked
/checked+
/checked-
Aritmetik tasma islemlerinde istisnai bir durumun olusup
olusmayacağını bildiren parametredir. Varsayılan olarak bu
aktif durumda değildir. Kaynak kod içerisinde bu islemi
checked anahtar sözcüklerini kullanarakta yapabiliriz. Eğer
tasam oldugunda istisnai durumun olusmasını istiyorsak
/checked yada /checked+ parametresini kullanmalıyız.
/bugreport:dosya_adi
Derleme sırasında kaynak kodda olusabilecek problemlerin
ve bu problemlerin önerilen çözümlerinin belirtilen dosyaya
yazdırılmasını sağlayan parametredir. Bu parametre
ilebelirtilen dosyaya çesitli derleme çıktılarıda eklenir.
/unsafe
Kaynak kodda unsafe anatar sözcüğünün kullanımını geçerli
kılınmasını sağlayacak parametredir. Göstericileri kullanmak
için unsafe anahtar sözcüğünü kullanmamız gerektiğini
hatırlayın.
/recurse:dir
/recurse:file
Derleme islemine katılacak kaynak kodların alt klasörlerde
aranmasını sağlayacak parametrelerdir. dir seçeneği ile
aramaya baslanacak klasör belirtilir. Bu seçenek ile
belirtilen klasör projenin varsayılan çalısma kalsörüdür.
Eğer file seçeneği kullanılırsa bu durumda belirtilen dosya
için arama yapılacaktır. Bu seçenekte wildcard dediğimiz *
karakteri kullanılabilmektedir.
/main:sınıf_adi
Eğer kaynak kod dosyamızda birden fazla Main() metodu
var ise programımızın hangi sınıftaki Main dosyasından
baslayacağını belirten parametredir. Bu da kaynak
kodumuzda birden fazla Main metodunun bulunabileceğinin
göstergesidir.
/nologo
Derleme sonrasında ekranda gösterilen derleyici bilgilerinin
kullanıcıya gösterilmemesini sağlayan parametredir.
Kanımca çok faydalı olmayan bir parametredir.
/help
yada
/?
Derleyici parametreleri ile ilgili yardım bilgilerinin
görüntülenmesini sağlayan parametrelerdir.
/incremental
/incremental+
/incrementalyada
/incr
/incr+
/incr-
Derleme isleminin optimize edilmis biçimde meydana
gelmesini sağlayan parametrelerdir. Söyleki, bir önceki
derleme bilgileri .dbg ve .pdb dosylarında tutularak yeni
derleme islemlerinde sadece değistirilen metotların
yeninden derlenmesi sağlanır. Farklı iki derleme islemi
arasındaki farklar ise .incr dosyasında saklanır. Varsayılan
olarak bu parametre aktif durumda değildir. /incremental
ile /incremental+ parametreleri esdeğerdedir.
/codepage:id_no
Derleme islemlerine katılacak kaynak kod dosyaları için bir
karakter kodlaması numarası alan parametredir. Bu
parametre daha çok kaynak kod dosyalarındaki
karakterlerin sizin sisteminizde bulunmayan karakter
kodlamasına denk düstüğü durumlarda kullanılır.
/baseaddress:adres
Yüklenecek DLL lerin belirlenecek bir adresten itibaren
belleğe yüklenmesini sağlar. adres değeri 8,10 yada 16 lık
sayı düzeninde olabilir.
@dosya_adi
Bazı derleyici parametrelerini otomatik olarak derleyiciye
bildirmek için bu parametrelerin önceden yazıldığı dosyayı
bildirmek için kullanılan parametredir.
/linkresource:dosya_adi
/linkres:dosya_adi
Belirtilen .NET kaynak(resource) dosyasına bir bağlantı
olusturmak için bu parametreler kullanılabilir.
/resource:dosya_adi
/res:dosya_adi
Belirtilen .NET kaynak(resource) dosyasını çıktı dosyasına
gömmek için kullanılan parametredir. Birden fazla kaynak
dosyası gömülecekse ; karakteri ile ayırmak gerekir.
/win32icon:dosya_adi
Belirtilen Win32 ikon dosyasını çıktı dosyasına eklemek için
kullanılan parametredir.
/win32res:dosya_adi
Belirtilen Win32 kaynak(resource) dosyasını(.res) çıktı
dosyasına eklemek için kullanılan parametredir.
Bu yazıda C# komut derleyicisinin paramtrelerini ve kullanımlarını inceledik. Yukarıdaki
tablonun sizin için iyi bir referans kaynağı olacağını umuyorum.
Boxing(Kutulamak) ve Unboxing(Kutuyu Kaldırmak)
Bugünkü makalemizde, Boxing ve Unboxing kavramlarını incelemeye çalısacağız. Boxing,
değer türünden bir değiskeni referans türünden bir nesneye aktarmaktır. Unboxing islemi
ise bunun tam tersidir. Yani referans türü değiskenin isaret ettiği değeri tekrar , değer
türü bir değiskene aktarmaktır. Bu tanımlarda karsımıza çıkan ve bilmemiz gereken en
önemli noktalar, değer türü değiskenler ile referans türü nesnelerin bellekte tutulus
sekilleridir.
.Net ortamında iki tür veri tipi vardır. Referans tipleri (reference type) ve değer tipleri
(value type). Đki veri türünün bellekte farklı sekillerde tutulmaları nedeni ile boxing ve
unboxing islemleri gündeme gelmistir. Bu nedenle öncelikle bu iki farklı veri tipinin
bellekte tutulus sekillerini iyice anlamamız gerekmektedir.
Bu anlamda karsımıza iki önemli bellek bölgesi çıkar. Yığın (stack) ve öbek(heap). Değer
tipi değiskenler, örneğin bir integer değisken vb.. belleğin stack adı verilen kısmında
tutulurlar. .Net’te yer alan değer türleri asağıdaki tabloda yer almaktadır. Bu tiplerin
stack bölegesinde nasıl tutulduğuna iliskin açıklayıcı sekli de asağıda görebilirsiniz.
Value type ( Değer Tipler)
bool long
byte sbyte
char short
decimal Struct ( Yapılar )
double uint
Enum ( Numaralandırıcılar ) ulong
float ushort
int
Tablo 1. Değer Tipleri
sekil 1. Değer Tiplerinin Bellekte Tutulusu
sekildende görüldüğü gibi, Değer Türleri bellekte, Stack dediğimiz bölgede tutulurlar.
Simdi buraya kadar anlasılmayan bir sey yok. Đlginç olan reference( basvuru) tiplerinin
bellekte nasıl tutulduğudur. Adındanda anlasıldığı gibi reference tipleri asıl veriye bir
basvuru içerirler. Örneğin sınıflardan türettiğimiz nesneler bu tiplerdendir. Diğer basvuru
tipleri ise asağıdaki tabloda yer almakdadır.
Reference Type (
Basvuru Tipleri )
Class ( sınıflar )
Interface
(arayüzler )
Delegate (
delegeler )
Object
String
Tablo 2. Basvuru Tipleri
Simdi gelin basvuru türlerinin bellekte nasıl tutulduklarına bakalım.
sekil 2. Basvuru tiplerinin bellekte tutulusu.
Görüldüğü gibi,basvuru tipleri hakikatende isimlerinin layığını vermekteler. Nitekim asıl
veriler öbek’te tutulurken yığında bu verilere bir basvuru yer almaktadır.
Đki veri türü arasındaki bir farkta bu verilerle isimiz bittiğinde geri iade edilis sekilleridir.
Değer türleri ile isimiz bittiğinde bunların yığında kapladıkları alanlar otomatik olarak
yığına geri verilir. Ancak referans türlerinde sadece yığındaki basvuru sisteme geri
veririlir. Verilerin tutulduğu öbekteki alanlar, Garbage Collector’un denetimindedirler ve
ne zaman sisteme iade edilicekleri tam olarak bilinmez. Bu ayrı bir konu olmakla beraber
oldukça karmasıktır. Đlerleyen makalelerimizde bu konudan da bahsetmeye çalısacağım.
Değer türleri ile basvuru türleri arasındaki bu temel farktan sonra gelelim asıl konumuza.
.NET’te her sınıf aslında en üst sınıf olan Object sınıfından türer. Yani her sınıf aslında
System.Object sınıfından kalıtım yolu ile otomatik olarak türetilmis olur. Sorun object gibi
referans bir türe, değer tipi bir değerin aktarılmasında yasanır. .NET’te hersey aslında
birer nesne olarak düsünülebilir. Bir değer türünü bir nesneye atamaya çalıstığımızda,
değer türünün içerdiği verinin bir kopyasının yığından alınıp, öbeğe tasınması ve nesnenin
bu veri kopyasına basvurması gerekmektedir. Đste bu olay kutulama ( boxing ) olarak
adlandırılır. Bu durumu minik bir örnek ile inceleyelim.
using System;
namespace boxunbox
{
class Class1
{
static void Main(string[] args)
{
double db=509809232323;
object obj;
obj=db;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
db+=1;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
}
}
}
Kodumuzun çalısmasını inceleyelim. Db isimli double değiskenimiz bir değer tipidir.
Örnekte bu değer tipini object tipinden bir nesneye aktarıyoruz. Bu halde bu değerler
içerisindeki verileri ekrana yazdırıyoruz. Sonra db değerimizi 1 arttırıyor ve tekrar bu
değerlerin içeriğini ekrana yazdırıyoruz. Đste sonuç;
sekil 3 Boxing Đslemi
Görüldüğü gibi db değiskenine yapılan arttırım object türünden obj nesnemize
yansımamıstır. Çünkü boxing islemi sonucu, obj nesnesi , db değerinin öbekteki
kopyasına basvurmaktadır. Oysaki artım db değiskeninin yığında yer alan orjinal değeri
üzerinde gerçeklesmektedir. Bu islemi açıklayan sekil asağıda yer almaktadır.
sekil 4. Boxing Đslemi
Boxing islemi otomatik olarak yapılan bir islemdir. Ancak UnBoxing isleminde durum biraz
daha değisir. Bu kez , basvuru nesnemizin isaret ettiği veriyi öbekten alıp yığındaki bir
değer tipi alanı olarak kopyalanması söz konusudur. Đste burada tip uyusmazlığı denen
bir kavramla karsılasırız. Öbekten, yığına kopylanacak olan verinin, yığında kendisi için
ayrılan yerin aynı tipte olması veya öbekteki tipi içerebilecek tipte olması gerekmektedir.
Örneğin yukarıdaki örneğimize unboxing islemini uygulayalım. Bu kez integer tipte bir
değer türüne atama gerçeklestirelim.
using System;
namespace boxunbox
{
class Class1
{
static void Main(string[] args)
{
double db=509809232323;
object obj;
obj=db;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
db+=1;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
int intDb;
intDb=(int)obj;
Console.WriteLine(intDb.ToString());
}
}
}
Bu kodu çalıstırdığımızda InvalidCastException istisnasının fırlatılacağını görüceksiniz.
Çünü referenas tipimizin öbekte basvurduğu veri tipi integer bir değer için fazla büyüktür.
Bu noktada (int) ile açıkça dönüsümü bildirmis olsak dahi bu hatayı alırız.
sekil 5. InvalidCastException Đstisnası
Ancak küçük tipi, büyük tipe dönüstürmek gibi bir serbestliğimiz vardır. Örneğin,
using System;
namespace boxunbox
{
class Class1
{
static void Main(string[] args)
{
double db=509809232323;
object obj;
obj=db;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
db+=1;
Console.WriteLine(db.ToString());
Console.WriteLine(obj.ToString());
/*int intDb;
intDb=(int)obj;
Console.WriteLine(intDb.ToString());*/
double dobDb;
dobDb=(double)obj;
Console.WriteLine(dobDb.ToString());
}
}
}
Bu durumda kodumuz sorunsuz çalısacaktır. Çünkü yığında yer alan veri tipi daha büyük
boyutlu bir değer türünün içine koyulabilir. Đste buradaki aktarım islemi unboxing olarak
isimlendirilmistir. Yani boxing islemi ile kutulanmıs bir veri kümesi, öbekten alınıp tekrar
yığındaki bir alana konulmus, dolayısıyla kutudan çıkartılmıstır. Olayın grafiksel
açıklaması asağıdaki gibidir.
sekil 6. Unboxing Đslemi
Geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüsmek dileğiyle
hepinize mutlu günler dilerim.
C# ile Çok Kanallı(Multithread) Uygulamalar – 1
Bugünkü makelemiz ile birlikte threading kavramını en basit haliyle tanımaya çalısacağız,
sonraki makalelerimizde de threading kavramını daha üst seviyede islemeye çalısacağız.
Bugün hepimiz bilgisayar basındayaken aynı anda pek çok uygulamanın sorunsuz bir
sekilde çalıstığını görürüz. Bir belge yazarken, aynı zamanda müzik dinleyebilir, internet
üzerinden program indirebilir ve sistemimizin kaynaklarının elverdiği ölçüde uygulamayla
eszamanlı olarak çalısabiliriz. Bu bize, günümüz islemcilerinin ve üzerlerinde çalısan
isletim sistemlerinin ne kadar yetenekli oluğunu gösterir. Gösterir mi acaba?
Aslında tek islemcili makineler günümüzün modern sihirbazları gibidirler. Gerçekte çalısan
uygulamaların tüm islemleri aynı anda gerçeklesmemektedir. Fakat islemciler öylesine
büyük saat hızlarına sahiptirlerki. islemcinin yaptığı, çalıstırılan uygulamaya ait islemleri
is parçacacıkları(thread) halinde ele almaktır. Her bir is parçacağı bir islemin birden fazla
parçaya bölünmesinden olusur. Đslemciler her is parçacığı için bir zaman dilimi belirler. T
zaman diliminde bir islem parçacığı yürütülür ve bu zaman dilim bittiğinde islem parçacığı
geçici bir süre için durur. Ardından kuyrukta bekleyen diğer is parçacağı baska bir zaman
dilimi içinde çalıstırılır. Bu böylece devam ederken, islemcimiz her is parçacığına geri
döner ve tüm is parçacıkları sıra sıra çalıstırılır. Dedik ya, islemciler bu islemleri çok
yüksek saat ve frekans hızında gerçeklestirir. Đste bu yüksek hız nedeniyle tüm bu olaylar
saniyenin milyon sürelerinde gerçeklesir ve sanki tüm bu uygulamalar aynı anda çalısıyor
hissi verilir.
Gerçektende uygulamaları birbirleriyle paralel olarak ve es zamanlı çalıstırmak aslında
birden fazla islemciye sahip sistemler için gerçeklenir.
Bugünkü uygulamamız ile, bahsetmis olduğumuz threading kavramına basit bir giris
yapıcağız. Nitekim threading kavramı ve teknikleri, uygulamalarda profesyonel olarak kod
yazmayı gerektirir. Daha açık sekilde söylemek gerekirse bir uygulama içinde yazdığımız
kodlara uygulayacağımız thread'ler her zaman avantaj sağlamaz. Bazı durumlarda
dezavantaja dönüsüp programların daha yavas çalısmasına neden olabilir. Nitekim
thread'lerin çalısma mantığını iyi kavramak ve uygulamalarda titiz davranmak gerekir.
Örneğin thread'lerin zaman dilimlerine bölündüklerinde sistemin nasıl bir önceki veya
daha önceki thread'i çalıstırabildiğini düsünelim. Đslemci zaman dilimini dolduran bir
thread için donanımda bir kesme isareti bırakır, bunun ardından thread'e ait bir takım
bilgiler belleğe yazılır ve sonra bu bellek bölgesinde Context adı verilen bir veri yapısına
depolanır. Sistem bu thread'e döneceği zaman Context'te yer alan bilgilere bakar ve
hangi donanımın kesme sinyali verdiğini bulur. Ardından bu sinyal açılır ve islemin bir
sonraki islem parçacığının çalısacağı zaman dilimine girilir. Eğer thread islemini çok fazla
kullanırsanız bu durumda bellek kaynaklarınıda fazlası ile tüketmis olursunuz. Bu
thread'leri neden titiz bir sekilde programlamamız gerektiğini anlatan nedenlerden sadece
birisidir. Öyleki yanlıs yapılan thread programlamaları sistemlerin kilitlenmesine dahi yol
açacaktır.
Threading gördüğünüz gibi çok basit olmayan bir kavramdır. Bu nedenle olayı daha iyi
açıklayabileceğimi düsündüğüm örneklerime geçmek istiyorum. Uygulamamızın formu
asağıdaki sekildeki gibi olacak.
Sekil 1. Form Tasarımımız.
Simdi kodlarımızı yazalım.
public void z1()
{
for(int i=1;i<60;++i)
{
zaman1.Value+=1;
for(int j=1;j<10000000;++j)
{
j+=1;
}
}
}
public void z2()
{
for(int k=1;k<100;++k)
{
zaman2.Value+=1;
for(int j=1;j<25000000;++j)
{
j+=1;
}
}
}
private void btnBaslat_Click(object sender, System.EventArgs e)
{
z1();
z2();
}
Program kodlarımızı kısaca açıklayalım. z1 ve z2 isimli metodlarımız progressBar
kontrolllerimizin değerlerini belirli zaman aralıklarıyla arttırıyorlar. Bu islemleri
geçeklestirmek için, Baslat baslıklı butonumuza tıklıyoruz. Burada önce z1 daha sonrada
z2 isimli metodumuz çalıstırılıyor. Bunun sonucu olarak önce zaman1 isimli progressBar
kontrolümüz doluyor ve dolması bittikten sonra zaman2 isimli progressBar
kontrolümüzün value değeri arttırılarak dolduruluyor.
Simdi bu programın söyle çalısmasını istediğimizi düsünelim. Her iki progressBar'da aynı
anda dolmaya baslasınlar. Đstediğimiz zaman z1 ve z2 metodlarının çalısmasını
durduralım ve tekrar baslatabilelim. Tekrar baslattığımızda ise progressBar'lar kaldıkları
yerden dolmaya devam etsinler. Sözünü ettiğimiz aslında her iki metodunda aynı anda
çalısmasıdır. Đste bu isi basarmak için bu metodları sisteme birer is parçacağı ( thread )
olarak tanıtmalı ve bu thread'leri yönetmeliyiz.
.Net ortamında thread'ler için System.Threading isim uzayını kullanırız. Öncelikle
programımıza bu isim uzayını ekliyoruz. Ardından z1 ve z2 metodlarını birer is parçacığı
olarak tanımlamamız gerekiyor. Đste kodlarımız.
public void z1()
{
for(int i=1;i<60;++i)
{
zaman1.Value+=1;
for(int j=1;j<10000000;++j)
{
j+=1;
}
}
}
public void z2()
{
for(int k=1;k<100;++k)
{
zaman2.Value+=1;
for(int j=1;j<25000000;++j)
{
j+=1;
}
}
}
private void btnBaslat_Click(object sender, System.EventArgs e)
{
z1();
z2();
}
ThreadStart ts1;
ThreadStart ts2;
Thread t1;
Thread t2;
private void btnBaslat_Click(object sender, System.EventArgs e)
{
ts1=new ThreadStart(z1); /* ThreadStart is parçacığı olarak kullanılıcak metod için
bir temsilcidir. Bu metod için tanımlanacak thread sınıfı nesnesi için paramtere olacak ve
bu nesnenin hangi metodu is parçacığı olarak göreceğini belirtecektir. */
ts2=new ThreadStart(z2);
t1=new Thread(ts1);
t2=new Thread(ts2);
t1.Start(); /* Đs parçağını Start metodu ile baslatıyoruz. */
t2.Start();
btnBaslat.Enabled=false;
}
private void btnDurdur_Click(object sender, System.EventArgs e)
{
t1.Suspend(); /* Đs parçacağı geçici bir süre için uyku moduna geçer. Uyku
modundaki bir is parçacağını tekrar aktif hale getirmek için Resume metodu kullanılır. */
t2.Suspend();
}
private void btnDevam_Click(object sender, System.EventArgs e)
{
t1.Resume(); /* Uyku modundaki is parçacığının kaldığı yerden devam etmesini
sağlar. */
t2.Resume();
}
private void btnKapat_Click(object sender, System.EventArgs e)
{
if(t1.IsAlive) /* Eğer is parçacıkları henüz sonlanmamıssa bunlar canlıdır ve IsAlive
özellikleri true değerine sahiptir. Programımızda ilk biten is parçacığı t1 olucağından onun
bitip bitmediğini kontrol ediyoruz. Eğer bitmis ise programımız close metodu sayesinde
kapatılabilir. */
{
MessageBox.Show("Çalısan threadler var program sonlanamaz.");
}
else
{
this.Close();
}
}
Uygulamamızda z1 ve z2 isimli metodlarımızı birer is parçacığı (thread) haline getirdik.
Bunun için System.Threding isim uzayında yer alan ThreadStart ve Thread sınıflarını
kullandık. ThreadStart sınıfı , is parçacığı olucak metodu temsil eden bir delegate gibi
davranır. Đs parçacıklarını baslatacak(start), durdurucak(suspend), devam
ettirecek(resume) thread nesnelerimizi tanımladığımız yapıcı metod ThreadStart
sınıfından bir temsilciyi parametre olarak alır. Sonuç itibariyle kullanıcı Baslat baslıklı
buton kontrolüne tıkladığında, her iki progressBar kontrolününde aynı zamanda dolmaya
basladığını ve ilerlediklerini görürüz. Bu asamada Durdur isimli button kontrolüne
tıklarsak her iki progressBar'ın ilerleyisinin durduğunu görürüz. Nitekim is parçacıklarının
Suspend metodu çağırılmıs ve metodların çalıstırılması durdurulmustur.
Sekil 2. Suspend metodu sonrası.
Bu andan sonra tekrar Devam buton kontrolüne tıklarsak thread nesnelerimiz Resume
metodu sayesinde çalısmalarına kaldıkları yerden devam ediceklerdir. Dolayısıyla
progressBar kontrolllerimizde kaldıkları yerden dolmaya devam ederler. Bu sırada
programı kapatmaya çalısmamız henüz sonlanmamıs is parçacıkları nedeni ile hataya
neden olur. Bu nedenle Kapat buton kontrolünde IsAlive özelliği ile is parçacıklarının canlı
olup olmadığı yani metodların çalısmaya devam edip etmediği kontrol edilir. Eğer
sonlanmamıssa kullanıcı asağıdaki mesaj kutusu ile uyarılır.
Sekil 3. Đs Parçacıkları henüz sonlanmamıs ise.
Evet geldik Threading ile ilgili makale dizimizin ilk bölümünün sonuna . Bir sonraki
makalemizde Threading kavramını daha da derinlemesine incelemeye çalısacağız.
Hepinize mutlu günler dilerim.
C# ile Çok Kanallı(Multithread) Uygulamalar – 2
Bugünkü makalemizde is parçacıklarının belli süreler boyunca nasıl
durgunlastırılabileceğini yani etkisizlestirebilieceğimizi islemeye çalısıcaz. Ayrıca is
parçacıklarının henüz sonlanmadan önce nasıl yokedildiklerini göreceğiz.
Bir önceki makalemizde hatırlayacak olursanız, is parçacıkları haline getirdiğimiz
metodlarımızda islemeleri yavaslatmak amacı ile bazı döngüler kullanmıstık. Gerçek
hayatta çoğu zaman, is parçacıklarının belirli süreler boyunca beklemesini ve süre sona
erdiğinde tekrardan islemelerine kaldığı yerden devam etmesini istediğimiz durumlar
olabilir. Önceki makalemizde kullandığımız Suspend metodu ile ilgili is parçacığını
durdurabiliyorduk. Bu ilgili is parçacıklarını geçici süre ile bekletmenin yollarından
birisidir. Ancak böyle bir durumda bekletilen is parçacığını tekrar hareketlendirmek
kullanıcının Resume metodunu çalıstırması ile olabilir. Oysaki biz, is parçacığımızın belli
bir süre boyunca beklemsini isteyebiliriz. Đste böyle bir durumda Sleep metodunu
kullanırız. Bu metodun iki adet overload edilmis versiyonu vardır.
public static void Sleep(int);
public static void Sleep(TimeSpan);
Biz bugünkü uygulamamızda ilk versiyonu kullanacağız. Bu versiyonda metodumuz
parametre olarak int tipinde bir değer almaktadır. Bu değer milisaniye cinsinden süreyi
bildirir. Metodun Static bir metod olduğu dikkatinizi çekmis olmalıdır. Static bir metod
olması nedeni ile, Sınıf adı ile birlikte çağırılmak zorundadır. Yani herhangibir thread
nesnesinin ardından Sleep metodunu yazamassınız. Peki o halde bekleme süresinin hangi
is parçacığı için geçerli olacağını nereden bileceğiz. Bu nedenle, bu metod is parçacığı
olarak tanımlanan metod blokları içerisinde kullanılır. Konuyu örnek üzerinden inceleyince
daha iyi anlayacağız. Metod çalıstırıldığında parametresinde belirtilen süre boyunca
geçerli is parçacığını bekletir. Bu bekleme diğer parçacıkların çalısmasını engellemez.
Süre sona erince, is parçacığımız çalısmasına devam edicektir. Simdi dilerseniz örnek bir
uygulama gelistirelim ve konuya açıklık getirmeye çalısalım.
Formumuzda bu kez üç adet ProgressBar kontrolümüz var. Baslat baslıklı düğmeye
bastığımızda is parçacıklarımız çalısıyor ve tüm ProgressBar'lar aynı anda değisik süreler
ile ilerliyor. Burada is parçacıkları olarak belirlediğimiz metodlarda kullandığımız Sleep
metodlarına dikkat edelim. Tabi kodlarımızı yazmadan önce System.Threading isim
uzayını eklemeyi unutmayalım.
Sekil 1. Form Tasarımımız.
public void pb1Ileri()
{
for(int i=1;i<100;++i)
{
pb1.Value+=1;
Thread.Sleep(800);
}
}
public void pb2Ileri()
{
for(int i=1;i<100;++i)
{
pb2.Value+=1;
Thread.Sleep(500); /* Metodumuz is parçacığı olarak basladıktan sonra döngü
içince her bir artımdan sonra 500 milisaniye bekler. */
}
}
public void pb3Ileri()
{
for(int i=1;i<100;++i)
{
pb3.Value+=1;
Thread.Sleep(300);
}
}
/* ThreadStart temsilcilerimiz ve Thread nesnelerimizi tanımlıyoruz. */
ThreadStart ts1;
ThreadStart ts2;
ThreadStart ts3;
Thread t1;
Thread t2;
Thread t3;
private void btnBaslat_Click(object sender, System.EventArgs e)
{
/* ThreadStart temsilcilerimizi ve Thread nesnelerimizi olusturuyoruz. */
ts1=new ThreadStart(pb1Ileri);
ts2=new ThreadStart(pb2Ileri);
ts3=new ThreadStart(pb3Ileri);
t1=new Thread(ts1);
t2=new Thread(ts2);
t3=new Thread(ts3);
/* Thread nesnelerimizi start metodu ile baslatıyoruz. */
t1.Start();
t2.Start();
t3.Start();
}
Uygulamamızı çalıstıralım. Her is parçacığı Sleep metodu ile belirtilen süre kadar
beklemeler ile çalısmasına devam eder. Örneğin pb3Ileri metodunda is parçacığımız
ProgressBar'ın Value değerini her bir arttırdıktan sonra 300 milisaniye bekler ve döngü bir
sonraki değerden itibaren devam eder. Sleep metodu ile Suspend metodları arasında
önemli bir bağ daha vardır. Bildiğiniz gibi Suspend metodu ilede bir is parçacığını
durdurabilmekteyiz. Ancak bu is parçacığını tekrar devam ettirmek için Resume
metodunu kullanmamız gerekiyor. Bu iki yöntem arasındaki fark idi. Diğeri önemli olgu
ise sudur; bir is parçacığı metodu içinde, Sleep metodunu kullanmıs olsak bile, programın
herhangibir yerinden bu is parçacığı ile ilgili Thread nesnesinin Suspend metodunu
çağırdığımızda, bu is parçacığı yine duracaktır. Bu andan itibaren Sleep metodu
geçerliliğini, bu is parçacığı için tekrardan Resume metodu çağırılıncaya kadar
kaybedecektir. Resume çağrısından sonra ise Sleep metodları yine islemeye devam eder.
Sekil 2. Sleep Metodunun Çalısması
Simdi gelelim diğer konumuz olan bir is parçacığının nasıl yok edileceğine. Bir is
parçacığını yoketmek amacı ile Abort metodunu kullanabiliriz. Bu metod çalıstırıldığında
derleyici aslında bir ThreadAbortException istisnası üretir ve is parçacığını yoketmeye
zorlar. Abort yöntemi çağırıldığında, ilgili is parçacığını tekrar resume gibi bir komutla
baslatamayız. Diğer yandan Abort metodu is parçacığı ile ilgili metod için
ThreadAbortException istisnasını fırlattığında (throw) , bu metod içinde bir
try..catch..finally korumalı bloğunda bu istisnayı yakalayabiliriz veya Catch bloğunda hiç
bir sey yazmas isek program kodumuz kesilmeden çalısmasına devam edicektir.
Abort metodu ile bir is parçacığı sonlandırıldığında, bu is parçacığını Start metodu ile
tekrar çalıstırmak istersek;
"ThreadStateException' Additional information: Thread is running or terminated; it can
not restart."
hatasını alırız. Yani is parçacığımızı tekrar bastan baslatmak gibi bir sansımız yoktur.
Simdi bu metodu inceleyeceğimiz bir kod yazalım. Yukarıdaki uygulamamızı asağıdaki
sekilde gelistirelim.
Sekil 3. Form Tasarımımız.
Kodlarımıza geçelim.
public void pb1Ileri()
{
try
{
for(int i=1;i<100;++i)
{
pb1.Value+=1;
Thread.Sleep(800);
}
}
catch(ThreadAbortException hata)
{
}
finally
{
}
}
public void pb2Ileri()
{
for(int i=1;i<100;++i)
{
pb2.Value+=1;
Thread.Sleep(500); /* Metodumuz is parçacığı olarak basladıktan sonra döngü
içince her bir artımdan sonra 500 milisaniye bekler. */
}
}
public void pb3Ileri()
{
for(int i=1;i<100;++i)
{
pb3.Value+=1;
Thread.Sleep(300);
}
}
/* ThreadStart temsilcilerimiz ve Thread nesnelerimizi tanımlıyoruz. */
ThreadStart ts1;
ThreadStart ts2;
ThreadStart ts3;
Thread t1;
Thread t2;
Thread t3;
private void btnBaslat_Click(object sender, System.EventArgs e)
{
/* ThreadStart temsilcilerimizi ve Thread nesnelerimizi olusturuyoruz. */
ts1=new ThreadStart(pb1Ileri);
ts2=new ThreadStart(pb2Ileri);
ts3=new ThreadStart(pb3Ileri);
t1=new Thread(ts1);
t2=new Thread(ts2);
t3=new Thread(ts3);
/* Thread nesnelerimizi start metodu ile baslatıyoruz. */
t1.Start();
t2.Start();
t3.Start();
btnBaslat.Enabled=false;
btnDurdur.Enabled=true;
btnDevam.Enabled=false;
}
private void btnDurdur_Click(object sender, System.EventArgs e)
{
t1.Abort(); /* t1 isimli Thread'imizi yokediyoruz. Dolayısıyla pb1Ileri isimli
metodumuzunda çalısmasını sonlandırmıs oluyoruz. */
/* Diğer iki is parçacığını uyutuyoruz. */
t2.Suspend();
t3.Suspend();
btnDurdur.Enabled=false;
btnDevam.Enabled=true;
}
private void btnDevam_Click(object sender, System.EventArgs e)
{
/* Đs parçacıklarını tekrar kaldıkları yerden çalıstırıyoruz. Đste burada t1 thread
nesnesini Resume metodu ile tekrar kaldığı yerden çalıstıramayız. Bu durumda
programımız hataya düsecektir. Nitekim Abort metodu ile Thread'imiz sonlandırılmıstır.
Aynı zamanda Start metodu ile Thread'imizi bastanda baslatamayız.*/
t2.Resume();
t3.Resume();
btnDurdur.Enabled=true;
btnDevam.Enabled=false;
}
Uygulamamızı deneyelim.
Sekil 4. Uygulamanın çalısması sonucu.
Değerli okurlarım geldik bir makalemizin daha sonuna. Bir sonraki makalemizde de
Threading konusunu islemeye devam edeceğiz. Hepinize mutlu günler dilerim.
Visual Basic'ten C#'a Geçis
Merhaba, bunca zamandır Visual Basic kullanan biri olarak .NET'e geçerken Visual Basic'i
seçtim. Daha çok Web Programıyla ilgilendiğim için internette yyaptığım aramalarda aspx
örneklerinin büyük bir çoğunluğunun C# ile hazırlandığını gördüm. Daha sonra bazı
makaleler kurcaladım. C#'ın geleceğin dili olduğu fikrine vardım.Elime geçen bir kaç ufak
kod üzerinde inceleme yapmaya basladım. Yaptıklarımı sizlerle paylasmak istedim. Bunu
yaparken VB.Net'i orta seviye bilen, C#'ı hiç bilmeyen programcıları düsünerek
hazırladım.
Đlk dikkatimizi çekecek olan Syntax(Sözdizimi) olacaktır. VB ile en önemli farklılık
buradan geliyor. Bu noktada bazı önemli noktaları açıklayacağım.
Yorum satırları: Yorum yazmak için iki farklı metod var. Đlki tek satır diğeri blok halinde
yazılır.
//Bu bir satırlık yorumdur.
/* Bu da blok halinde
Hazırlanmıs bir yorumdur.*/
Yukarıda bulunan her iki örneğimiz de derleyici tarafından gözardı edilecektir. Yorum
kullanmak kodlarınızın anlasıbilirliğini arttırdığı için mutlaka kullanılmalıdır. Bunu da
belirtmeden geçmeyim.
Değiskenlerimizi VB'ye göre farklı sekillerde tanımlıyoruz. VB'de değiskenlerle aranızda
problem yoksa emin olun C#'ta da olmayacaktır.
int a;
ile tek bir değisken tanımlayabiliyoruz. Aynı türde bir kaç değisken tanımlamak için
int a,b,c;
kullanabilirsiniz. Visual Basic'e .Net ile eklenen değiskene isim atanırken değer belirtmeyi
de C# da kullanabiliyoruz.
int a=3;
"Merhaba dünya" demek için ilk adımlarımızı atalım. Đlk olarak VS.Net ile Merhaba dünya
demeyi öğrenelim.VS.Net ile yeni bir proje açın ama bu sefer C# projesi olsun. Formun
ortasına kocaman bir tus koyalım. Çift tıklayıp alttaki koda göre uyarlayın.
private void button1_Click(object sender, System.EventArgs e)
{
MessageBox.Show("Selam","Merhaba Dünya");
}
Đlk satırda button1'in click olayı olduğunu belirttik. Tahmin ettiğiniz gibi : bir mesaj
kutusu açılacak ve merhaba dünya diyecek. Merhaba Dünya baslığı olacak. Đçinde sadece
selam yazacak.
Hemen gözümüze çarpanlar Süslü parantez ve noktalı virgül olmustur.C#'da her olay
süslü parantez içinde yer alıyor. Bir if döngüsü veya örnekteki buttonclick olayı. Ve
hemen her satırın sonunda noktalı virgül konuyor. Eğer bilgisayarınız kod yazarken size
syntax hatası verirse ilk bunları kontrol edin. Alısması gerçekten zor oluyor.
Bir daha ki konuda konuları biraz daha toplayıp bir uygulama ve bir aspx sayfası
hazırlayacağız.
Herkese çalısmalarında basarılar dilerim.
Tuğrul ARAS
C# ile Çok Kanallı(Multithread) Uygulamalar – 3
Đs parçacıklarını islediğimiz yazı dizimizin bu üçüncü makalesinde, is parçacıklarının birbirlerine
karsı öncelik durumlarını incelemeye çalısacağız. Đs parçacıkları olarak tanımladığımız metodların
çalısma sıralarını, sahip oldukları öneme göre değistirmek durumunda kalabiliriz. Normal sartlar
altında, olusturduğumuz her bir is parçacığı nesnesi aynı ve esit önceliğe sahiptir. Bu öncelik değeri
Normal olarak tanımlanmıstır. Bir is parçacığının önceliğini değistirmek istediğimizde, Priority
özelliğinin değerini değistiririz. Priority özelliğinin .NET Framework'teki tanımı asağıdaki gibidir.
public ThreadPriority Priority {get; set;}
Özelliğimiz ThreadPriority numaralandırıcısı (enumerator) tipinden değerler almaktadır. Bu değerler
asağıdaki tabloda verilmistir.
Öncelik Değeri
Highest
AboveNormal
Normal
BelowNormal
Lowest
Tablo 1. Öncelik(Priority) Değerleri
Programlarımızı yazarken, is parçacıklarının çalısma sekli verilen öncelik değerlerine göre
değisecektir. Elbette tahmin edeceğiniz gibi yüksek öncelik değerlerine sahip olan is parçacıklarının
isaret ettikleri metodlar diğerlerine göre daha sık aralıklarda çağırılacak, dolayısıyla düsük öncelikli
is parçacıklarının referans ettiği metodlar daha geç sonlanacaktır. Simdi olayı daha iyi
canlandırabilmek için asağıdaki örneğimizi gelistirelim.
Daha önceden söylediğimiz gibi, bir is parçacığının Priority özelliğine her hangibir değer vermez
isek, standart olarak Normal kabul edilir. Buda tüm is parçacıklarının varsayılan olarak esit
önceliklere sahip olacakları anlamına gelmektedir. Simdi asağıdaki formumuzu olusturalım.
Uygulamamız iki is parçacığına sahip. Bu parçacıkların isaret ettiği metodlardan birisi 1' den 1000' e
kadar sayıp bu değerleri bir label kontrolüne yazıyor. Diğeri ise 1000' den 1' e kadar sayıp bu
değerleri baska bir label kontrolüne yazıyor. Formumuzun görüntüsü asağıdakine benzer olmalıdır.
Sekil 1. Form Tasarımımız.
Simdide program kodlarımızı yazalım.
/* Bu metod 1' den 1000' e kadar sayar ve değerleri lblSayac1 isimli label kontrolüne yazar.*/
public void Say1()
{
for(int i=1;i<1000;++i)
{
lblSayac1.Text=i.ToString();
lblSayac1.Refresh(); /* Refresh metodu ile label kontrolünün görüntüsünü tazeleriz. Böylece
herbir i değerinin label kontrolünde görülebilmesini sağlamıs oluyoruz. */
for(int j=1;j<90000000;++j)
{
j+=1;
}
}
} /* Bu metod 1000' den 1' e kadar sayar ve değerleri lblSayac2 isimli label kontrolüne yazar.*/
public void Say2()
{
for(int i=1000;i>=1;i--)
{
lblSayac2.Text=i.ToString();
lblSayac2.Refresh(); for(int j=1;j<45000000;++j)
{
j+=1;
}
}
} /* ThreadStart ve Thread nesnelerimizi tanımlıyoruz. */ ThreadStart ts1;
ThreadStart ts2;
Thread t1;
Thread t2;
private void btnBaslat1_Click(object sender, System.EventArgs e)
{
/* Metodlarımızı ThreadStart nesneleri ile iliskilendiriyoruz ve ThreadStart nesnelerimizi
olusturuyoruz.*/
ts1=new ThreadStart(Say1);
ts2=new ThreadStart(Say2);
/* Đs parçacıklarımızı, ilgili metodların temsil eden ThreadStart nesnelerimiz ile olusturuyoruz.*/
t1=new Thread(ts1);
t2=new Thread(ts2);
/* Đs parçacıklarımızı çalıstırıyoruz.*/
t1.Start();
t2.Start();
btnBaslat1.Enabled=false;
btnIptal.Enabled=true;
}
private void btnIptal_Click(object sender, System.EventArgs e)
{
/* Đs parçacıklarımızı iptal ediyoruz. */
t1.Abort();
t2.Abort();
btnBaslat1.Enabled=true;
btnIptal.Enabled=false;
}
private void btnKapat_Click(object sender, System.EventArgs e)
{
/* Uygulamayı kapatmak istediğimizde, çalısan is parçacığı olup olmadığını kontrol ediyoruz.
Bunun için is parçacıklarının IsAlive özelliğinin değerlerine bakıyoruz. Nitekim kullanıcının,
herhangibir is parçacığı sonlanmadan uygulamayı kapatmasını istemiyoruz. Ya iptal etmeli yada
sonlanmalarını beklemeli. Đptal ettiğimizde yani Abort metodları çalıstırıldığında hatırlayacağınız
gibi, is parçacıklarının IsAlive değerleri false durumuna düsüyordu, yani iptal olmus oluyorlardı.*/
if((!t1.IsAlive) && (!t2.IsAlive))
{
Close();
}
else
{
MessageBox.Show("Hala kapatılamamıs is parçacıkları var. Lütfen bir süre sonra
tekrar deneyin.");
}
}
Uygulamamızda su an için bir yenilik yok aslında. Nitekim is parçacıklarımız için bir öncelik
ayarlaması yapmadık. Çünkü size göstermek istediğim bir husus var. Bir is parçacığı için
herhangibir öncelik ayarı yapmadığımızda bu değer varsayılan olarak Normal dir. Dolayısıyla her is
parçacığı esit önceliğe sahiptir. Simdi örneğimizi çalıstıralım ve kafamıza göre bir yerde iptal
edelim.
Sekil 2. Öncelik değeri Normal.
Ben 11 ye 984 değerinde islemi iptal ettim. Tekrar is parçacıklarını Baslat baslıklı butona tıklayıp
çalıstırırsak ve yine aynı yerde islemi iptal edersek, ya aynı sonucu alırız yada yakın değerleri elde
ederiz. Nitekim programımızı çalıstırdığımızda arka planda çalısan isletim sistemine ait pek çok is
parçacığıda çalısma sonucunu etkiler. Ancak asağı yukarı aynı veya yakın değerle ulasırız. Oysa bu
is parçacıklarının öncelik değelerini değistirdiğimizde sonuçların çok daha farklı olabilieceğini
söyleyebiliriz. Bunu daha iyi anlayabilmek için örneğimizi gelistirelim ve is parçacıklarının öncelik
değerleri ile oynayalım. Formumuzu asağıdaki gibi tasarlayalım.
Sekil 3. Formumuzun yeni tasarımı.
Artık is parçacıklarını baslatmadan önce önceliklerini belirleyeceğiz ve sonuçlarını incelemeye
çalısacağız. Kodlarımızı su sekilde değistirelim. Önemli olan kod satırlarımız, is parçacıklarının
Priority özelliklerinin değistiği satırlardır.
/* Bu metod 1' den 1000' e kadar sayar ve değerleri lblSayac1 isimli label kontrolüne yazar.*/
public void Say1()
{
for(int i=1;i<1000;++i)
{
lblSayac1.Text=i.ToString();
lblSayac1.Refresh(); /* Refresh metodu ile label kontrolünün görüntüsünü tazeleriz.
Böylece herbir i değerinin label kontrolünde görülebilmesini sağlamıs oluyoruz. */
for(int j=1;j<90000000;++j)
{
j+=1;
}
}
}
/* Bu metod 1000' den 1' e kadar sayar ve değerleri lblSayac2 isimli label kontrolüne yazar.*/
public void Say2()
{
for(int i=1000;i>=1;i--)
{
lblSayac2.Text=i.ToString();
lblSayac2.Refresh();
for(int j=1;j<45000000;++j)
{
j+=1;
}
}
}
ThreadPriority tp1;
/* Priority öncelikleri ThreadPriority tipindedirler. */
ThreadPriority tp2; /* OncelikBelirle metodu, kullanıcının TrackBar'da seçtiği değerleri göz önüne
alarak, is parçacıklarının Priority özelliklerini belirlemektedir. */
public void OncelikBelirle()
{
/* Switch ifadelerinde, TrackBar kontrollünün değerine göre , ThreadPriority değerleri
belirleniyor. */
switch(tbOncelik1.Value)
{
case 1:
{
tp1=ThreadPriority.Lowest; /* En düsük öncelik değeri. */
break;
}
case 2:
{
tp1=ThreadPriority.BelowNormal; /* Normalin biraz altı. */
break;
}
case 3:
{
tp1=ThreadPriority.Normal; /* Normal öncelik değeri. Varsayılan değer budur.*/
break;
}
case 4:
{
tp1=ThreadPriority.AboveNormal; /* Normalin biraz üstü öncelik değeri. */
break;
}
case 5:
{
tp1=ThreadPriority.Highest; /* En üst düzey öncelik değeri. */
break;
}
}
switch(tbOncelik2.Value)
{
case 1:
{
tp2=ThreadPriority.Lowest; /* En düsük öncelik değeri. */
break;
}
case 2:
{
tp2=ThreadPriority.BelowNormal; /* Normalin biraz altı. */
break;
}
case 3:
{
tp2=ThreadPriority.Normal; /* Normal öncelik değeri. Varsayılan değer budur.*/
break;
}
case 4:
{
tp2=ThreadPriority.AboveNormal; /* Normalin biraz üstü öncelik değeri. */
break;
}
case 5:
{
tp2=ThreadPriority.Highest; /* En üst düzey öncelik değeri. */
break;
}
/* Đs Parçacıklarımıza öncelik değerleri aktarılıyor.*/
t1.Priority=tp1;
t2.Priority=tp2;
}
/* ThreadStart ve Thread nesnelerimizi tanımlıyoruz. */
ThreadStart ts1;
ThreadStart ts2;
Thread t1;
Thread t2;
private void btnBaslat1_Click(object sender, System.EventArgs e)
{
/* Metodlarımızı ThreadStart nesneleri ile iliskilendiriyoruz ve ThreadStart nesnelerimizi
olusturuyoruz.*/
ts1=new ThreadStart(Say1);
ts2=new ThreadStart(Say2);
/* Đs parçacıklarımızı, ilgili metodların temsil eden ThreadStart nesnelerimiz ile
olusturuyoruz.*/
t1=new Thread(ts1);
t2=new Thread(ts2);
OncelikBelirle(); /* Öncelik ( Priority ) değerleri, is parçacıkları Start metodu ile baslatılmadan
önce belirlenmelidir. */
/* Đs parçacıklarımızı çalıstırıyoruz.*/
t1.Start();
t2.Start();
btnBaslat1.Enabled=false;
btnIptal.Enabled=true;
tbOncelik1.Enabled=false;
tbOncelik2.Enabled=false;
}
private void btnIptal_Click(object sender, System.EventArgs e)
{
/* Đs parçacıklarımızı iptal ediyoruz. */
t1.Abort();
t2.Abort();
btnBaslat1.Enabled=true;
btnIptal.Enabled=false;
tbOncelik1.Enabled=true;
tbOncelik2.Enabled=true;
}
private void btnKapat_Click(object sender, System.EventArgs e)
{
/* Uygulamayı kapatmak istediğimizde, çalısan is parçacığı olup olmadığını kontrol ediyoruz.
Bunun için is parçacıklarının IsAlive özelliğinin değerlerine bakıyoruz. Nitekim kullanıcının,
herhangibir is parçacığı sonlanmadan uygulamayı kapatmasını istemiyoruz. Ya iptal etmeli yada
sonlanmalarını beklemeli. Đptal ettiğimizde yani Abort metodları çalıstırıldığında hatırlayacağınız
gibi, is parçacıklarının IsAlive değerleri false durumuna düsüyordu, yani iptal olmus oluyorlardı.*/
if((!t1.IsAlive) && (!t2.IsAlive))
{
Close();
}
else
{
MessageBox.Show("Hala kapatılamamıs is parçacıkları var. Lütfen bir süre sonra tekrar
deneyin.");
}
}
Simdi örneğimizi çalıstıralım ve birinci is parçacığımız için en yüksek öncelik değerini (Highest)
ikinci is parçacığımız içinde en düsük öncelik değerini (Lowest) seçelim. Sonuçlar asağıdakine
benzer olucaktır.
Sekil 4. Önceliklerin etkisi.
Görüldüğü gibi öncelikler is parçacıklarının çalısmasını oldukça etkilemektedir. Geldik bir
makalemizin daha sonuna. Bir sonraki makalemizde is parçacıkları hakkında ilerlemeye devam
edeceğiz. Görüsmek dileğiyle hepinize mutlu günler dilerim.
XOR Operatörü ile Temel Bir Sifreleme Algoritması
Sifreleme günümüzde güvenli iletisim için çok önemli bir konuma gelmistir, uzun yıllardan
beri çok fazla sifreleme algortiması gelistirilmistir. Bu sifreleme algoritmalarının bir çoğu
.NET sınıf kütüphanesinde zaten varsayılan olarak bulunmaktadır, bu yazıda ise kendi
sifreleme algortimalarımızı nasıl olusturabileceğimiz görmek açısından temel bir sifreleme
algoritmasını sizlere göstereceğim.
Bir mesajın yada metnin sifrelenmesi genellikle sifrelenecek mesajın çesitli operatörler
yardımıyla farklı mesajlara dönüstürülmesi ile olmaktadır. Burada bilmemiz gereken
nokta sudur : sifrelenecek mesaj ile sifrelenmis mesajın aynı alfabeden sözcükleri
içermesidir. Örneğin ikili(binary) sayılardan olusturulan bir mesaj sifrelendiği takdirde
yine ikili bir sayı olacaktır. Sifreleme yapılırken genellikle anahtar dedğimiz yardımcı bir
mesajdan faydalanır. Mesajın sifrelenmesi bu anahtar ile gerçeklesmektedir. Aynı sekilde
sifrelenmis mesajın çözülmesinde de bu anahtar kullanılmaktadır. Sifreleme islemi ise bir
yada daha fazla operatör sayesinde yapılmaktadır. Buradaki operatörler tekil bir operatör
olabileceği gibi kullanıcının tanımlayacağı karmasık değiskenli operatörler de olabilir.
Sifreleme programını yazanlar genellikle sifreyi çözen programıda yazmak zorunda
kalırlar. Nede olsa sifreler çözülmek içindir. Çözülemeyen sifreli mesajların pek bir anlamı
olmayacağı açıktır. Her ne kadar sifreleme ve sifre çözme programları birbirnin tersi de
olsa iki farklı program yazmak yinede zaman kaybettirir. Aynı programın hem sifreleyici
hemde sifre çözücü olduğu bir sistem herhalde hepimizin ilgisini çekecektir. Bu yazıda
hem sifre çözücü hemde sifreleme isine yarayacak özel bir operatör olan XOR(Bitsel Özel
Veya) operatörünü ve bu operatörü kullanarak nasıl sifreleyici ve aynı zamanda sifre
çözücü bir programı gelistirebileceğimizi inceleyeceğiz.
XOR(Bitsel Özel Veya) Operatörü
"Özel veya" operatörü iki operandı olan bir operatördür. Özel veya operatörü aldığı
operandlarının bütün bitlerini karsılıklı olarak "özel veya(XOR)" islemine tutar. Đsterseniz
birçoğumzun matematik derslerinden hatırlayacağı "özel veya" yani XOR isleminin
tanımını görelim. Özel veya operatörü iki operand aldığı için dört farklı durum
sözkonusudur. Bu durumlar ve sonuçları asağıdaki tabloda belirtilmistir.
Operand 1 Operand 2 Sonuç
1 1 0
1 0 1
0 1 1
0 0 0
Tablodan da görüldüğü özere XOR operatörünün sonucu ancak ve ancak her iki operand
da birbirine esitse 1 değerini almaktadır. Bu sonuç bize sifreleme algoritmasında büyük
bir kolaylık sağlayacaktır. XOR operatörü bitsel bir operatör olduğu için her iki operandın
da ikili bir sayı olması gerekir. C#'taki veri türleri ile XOR operatörü kullanıldığında veriyi
olusturan her bir değiskenin bütün bitleri karsılıklı olarak XOR islemine tabi tutulur.
Örneğin byte türünden 1 sayısı ile yine byte türünden 2 sayının XOR islemin sonra hangi
değeri olusturacağını görelim.
Öncelikle 1 ve 2 sayısının bitsel açılımını yazalım :
Not : 1 byte büyüklüğünün 8 bite denk düstüğünü hatırlayalım.
1 --> 0 0 0 0 0 0 0 1
2 --> 0 0 0 0 0 0 1 0
--------------------- 1 ^ 2 (Not : XOR operatörünün simgesi ^ karakteridir.)
3 --> 0 0 0 0 0 0 1 1
Dolayısıyla 1 ve 2 değerini XOR islemine tabi tutarsak 3 değerini elde ederiz. Bu sonucu
programlama yoluyla elde etmek için bir konsol uygulaması açın ve asağıdaki ifadeyi
ekrana yazdırın.
Console.WriteLine((1^2));
XOR operatörünün diğer önemli bir özelliği ise geri dönüsümlü bir operatör olmasıdır.
Yani bir sayıyı "özel veya" islemine tabi tuttuktan sonra sonucu yine aynı sayı ile "özel
veya" islemine tabi tutarsak baslangıçtaki sonucu elde ederiz. Örneğin 3 sayısını 1 ile
"özel veya" islemine tabi tutarsak 2 sayısını, 2 ile "özel veya" islemine tabi tutarsak bu
sefer 1 sayısını elde ederiz. Bu özelliği bir formül ile gösterirsek;
x = z ^ b;
y = x ^ b;
ise
z = y dir.
XOR isleminin bu özelli yazdığımız programa hem sifre çözücü hemde sifreleyici olma
özelliği katacaktır.
Sifreleyici ve Sifre Çözücü Program
Bu bölümde sifre çözücü ve aynı zamanda sifreleyeci programı XOR operatörünü
kullanarak gelistireceğiz. Program bir dosya sifreleyicisi ve sifre çözücüsü olarak
kullanılacaktır. Sifrelenecek dosya bir metin dosyası, çalıstırılabilir exe dosyası olabileceği
gibi bir video ve resim dosyasıda olabilir. Çünkü XOR islemini dosyayı olusturan byte'lar
düzeyinde gerçeklestireceğiz. Sifreleme islemi yaparken dosyadaki her bir byte sırayla
kullanıcının gireceği bir anahtardan elde edilen sayı ile XOR islemine tabi tutulacaktır.
XOR islemi sayesinde yazdığımız program aynı zamanda bir sifre çözücü program olarak
ta çalısacaktır. Đlk olarak programımızın en temel halini yazalım ardından programız
üzerinde iyilestirme çalısması yapacağız.
Kaynak kodları asağıda verilen programı yazın ve derleyin.
using System;
using System.IO;
namespace XOR
{
class csharpnedir
{
static void Main(string[] args)
{
if(args.Length != 2)
{
Console.WriteLine("Hatalı kullanım");
Console.WriteLine("Örnek kullanım : Sifrele xx.text anahtar");
return ;
}
string kaynakDosya = args[0];
string hedefDosya = args[1];
string anahtar = "";
Console.Write("Anahtarı girin :");
anahtar = Console.ReadLine();
int XOR = 0;
for(int i = 0; i
XOR = XOR + (int)(anahtar[i]);
FileStream fsKaynakDosya = new FileStream(kaynakDosya,FileMode.Open);
FileStream fsHedefDosya = new FileStream(hedefDosya,FileMode.CreateNew |
FileMode.CreateNew,FileAccess.Write);
int kaynakByte;//(3 byte'lık 0 dizisi + kaynakByte)
byte hedefByte;
while((kaynakByte = fsKaynakDosya.ReadByte()) != -1)
{
hedefByte = (byte)((int)kaynakByte ^ XOR);
fsHedefDosya.WriteByte(hedefByte);
}
fsHedefDosya.Close();
fsKaynakDosya.Close();
}
}
}
Hemen programın sonucunu görelim :
Asağıdaki gibi gizlilik derecesi yüksek olan bir metin dosyası olusturun.
Not : Sifrelenecek dosyanın metin tabanlı olması zorunlu değildir. Çünkü sifreleme
islemini karakter tabanlı değil byte düzeyinde yapmaktayız. Ama sonuçlarını daha iyi
görebilmek için örneği metin tabanlı dosya üzerinde gösteriyorum.
Programı asağıdaki gibi komut satırından çalıstırın.
Programı çalıstırdıktan sonra olusturulan Sifreli isimli dosyayı Notepad programında
görüntülediğimizde asağıdaki gibi bir ekran ike karsılasırız.
Dikkat edin, sifreleme islemini byte düzeyinde yaptığımız için sifreli dosya artık metin
dosyası değil binary bir dosya haline gelmistir.
Sifrelenmis dosyayı tekrar eski haline getirmek için tek yapmamız gereken komut
satırından sifreleme programını diğer bir deyisle sifre çözücü programını çalıstırmamız
gerekir. Anahtar olarak ta tabiki sifrelemede kullandığımız anahtar kullanmamız gerekir.
Komut satırından asağıdaki gibi programı çalıstırdığımızda orjinal metin dosyasını elde
edebiliriz.
XOR SifreliMesaj OrjinalMesaj.txt
Anahtarı Girin : XkuksAh
Gördüğünz gibi programımız hem sifreleyici hemde sifre çözücü olarak
kullanılabilmektedir.
Sonuçlar
Dikkat ederseniz mesaj dosyasının her byte değeri sabit bir değerle karsılıklı olarak XOR
islemine tabi tutulmustur. XOR islemine tabi tutulan değer kullanıcı tarafından girilen
anahtardan olusturulmustur. Anahtar değerin her bi karakterinin ASCII karsılığı
toplanarak elde edilen değer XOR isleminin sabit operandı olarak ele alınmıstır. Ancak
programımızda ufak bir sorun var. Çünkü sifrelemek için girilen anahtar değerini
olusturan karakterlerin hepsini içerecek sekilde olusturulan bütün kombinasyonlar
sifrelenmis dosyayı çözecektir. Örneğin sifrelemek için kullanılan anahtar değerin
"AxyHMnK2" olduğunu düsünelim. Bu durumda "xynAHMNK2" ve "2MnKHyxA" gibi
kombinasyonlar dosyanın çözülmesini sağlayacaktır.
Yukarıda bahsi geçen kısıtı engellemek için XOR islemine tabi tutulacak operandı anahtar
değerden elde ederlen farklı bir yöntem kullanılır. Bu operandı asağıdaki gibi yeniden elde
edebiliriz.
int XOR = 0;
for(int i = 0; i
XOR = XOR + (int)(anahtar[i] * 10);
Yukarıdaki düzenlmeye rağmen sifreyi çözecek anahtar tek değildir. Çünkü farklı karakter
kombinasyonlarının toplamı çok düsük bir ihtimalde olsa orjinal XOR değerine esit olabilir.
Ancak bu durum sifreleme tekniğinin güvenirliğini azaltmaz. Çünkü orjinal XOR değerinin
tahmin etme olsaılığı çok azdır.
Gelelim diğer bir kısıta : Dikkat ederseniz sifreleme yaparken dosyadaki her bir byte
değerini sabit bir değerle XOR islemine tabi tuttuk. Bir byte değiskenin sınırları 0- 255
arası olduğu için sifreleme programını çözmek için en fazla 256 ihtimal vardır. Tabi
burada anahtar değerden XOR islemine tabi tutulacak değerin nasıl elde edildiğinin
bilindiği varsayılmaktadır. Eğer bu yöntem bilinmiyorsa sifrenin çözülme olasılığı
neredeyse imkansızdır. XOR operandının elde edilme yönteminin bilindiği varsayımı
altında 256 sayısını yani sifrenenin çözülme olasılığını azaltmak için yapmamız gereken
XOR islemini 1 byte'lık bloklar yerine daha büyük bloklar ile yapmaktır. Örneğin XOR
islemini 4 byte lık veri blokları ile yaptığımızda XOR isleminin operandı 4.294.967.296
ihtimalden birisidir. Eğer XOR islemine sokulan veri bloğu artırılırsa operandın alabileceği
değerler üstel bir biçimde artacaktır. Bu arada XOR islemine sokulacak veri bloklarının
sayısı arttıkça xor islemindeki operandın değerini belirlemek için farklı yöntemler
kullanılmalıdır. Çünkü eğer asağıdaki yöntemde elde edilen XOR operandını kullanırsak 1
byte yada 4 byte'lık verilerle çalısmanın çok önemli bir farkı olmayacaktır. (Burada fark,
girilen anahtara göre belirlenir. Örneğin olusturulan xor operandı 256 değerinden küçük
ise hiç bir fark meydana gelmeyecektir.)
int XOR = 0;
for(int i = 0; i
XOR = XOR + (int)(anahtar[i]);
Bu yöntemle gelistirilecek bir sifreleme programını daha etkili hale getirmek için bir
yöntem daha vardır. Programı incelerseniz her bir byte bloğunu sabit bir değerle xor
islemine soktuk. Bu aslında biraz risklidir. Zira büyük bir sifreli metnin çok küçük
bölümünün çözülmesi tamamının çözülmesi anlamına gelir. Bu yüzden her bir byte
bloğunu farklı bir değerle xor islemine tabi tutarsak sifreli metnin her bir sifreli bloğu bir
diğerinden bağımsız hale gelir. Yani çözülmüs bir sifreli blok diğer bloğun çözülmesine
kesin bir bilgi vermez. Dolayısıyla sifre krıcı programların lineer bir yöntem izlemesi
engellenmis olur.
Bu tür bir sifreleme yönteminin devlet düzeyinde güvenli olması gereken mesajlarda
kullanılması uygun olmasada mesajların baskaları tarafından açıkca görülmeden
haberlesme sistemlerinden geçirilmesi için uygun bir yöntemdir. Elbetteki daha basit
yöntemlerle de bu islemi gerçeklestirebiliriz ancak bu yöntemin en önemli özelliği hem
sifreleme hemde sifre çözücü olarak kullanılabilmesidir.
Bu yazının kendi sifreleme algortimalarınızı olusturmada size yol gösterebileceğini
umuyor iyi çalısmalar diliyorum.
C# ile Çok Kanallı(Multithread) Uygulamalar – 4
Bundan önceki üç makalemizde is parçacıkları hakkında bilgiler vermeye çalıstım, bu makalemde
ise isimize yarayacak tarzda bir uygulama gelistirecek ve bilgilerimizi pekistireceğiz. Bir is
parçacığının belkide en çok ise yarayacağı yerlerden birisi veritabanı uygulamalarıdır. Bazen
programımız çok uzun bir sonuç kümesi döndürecek sorgulara veya uzun sürecek güncelleme
ifadeleri içeren sql cümlelerine sahip olabilir. Böyle bir durumda programın diğer öğeleri ile olan
aktivitemizi devam ettirebilmek isteyebiliriz. Ya da aynı anda bir den fazla is parçacığında, birden
fazla veritabanı islemini yaptırarak bu islemlerin tamamının daha kısa sürelerde bitmesini
sağlıyabiliriz. Đste bu gibi nedenleri göz önüne alarak bu gün birlikte basit ama faydalı olacağına
inandığım bir uygulama gelistireceğiz.
Olayı iyi anlayabilmek için öncelikle bir milat koymamız gerekli. Đs parçacığından önceki durum ve
sonraki durum seklinde. Bu nedenle uygulamamızı önce is parçacığı kullanmadan olusturacağız.
Sonrada is parçacığı ile. Simdi programımızdan kısaca bahsedelim. Uygulamamız asağıdaki sql
sorgusunu çalıstırıp, bellekteki bir DataSet nesnesinin referans ettiği bölgeyi, sorgu sonucu dönen
veri kümesi ile dolduracak.
SELECT Products.* From [Order Details] Cross Join Products
Bu sorgu çalıstırıldığında, Sql sunucusunda yer alan Northwind veritabanı üzerinden, 165936
satırlık veri kümesi döndürür. Elbette normalde böyle bir islemi istemci makinenin belleğine
yığmamız anlamsız. Ancak sunucu üzerinde çalısan ve özellikle raporlama amacı ile kullanılan
sorguların bu tip sonuçlar döndürmeside olasıdır. Simdi bu sorguyu çalıstırıp sonuçları bir DataSet'e
alan ve bu veri kümesini bir DataGrid kontrolü içinde gösteren bir uygulama gelistirelim. Öncelikle
asağıdaki formumuzu tasarlayalım.
Sekil 1. Form Tasarımımız.
Simdide kodlarımızı yazalım.
DataSet ds;
public void Bagla()
{
dataGrid1.DataSource=ds.Tables[0];
}
public void Doldur()
{
SqlConnection conNorthwind=new SqlConnection("data source=localhost;initial
catalog=Northwind;integrated security=sspi");
conNorthwind.Open();
SqlDataAdapter daNorthwind=new SqlDataAdapter("SELECT Products.* From [Order
Details] Cross Join Products",conNorthwind);
ds=new DataSet();
daNorthwind.Fill(ds);
conNorthwind.Close();
MessageBox.Show("DataTable dolduruldu...");
}
private void btnKapat_Click(object sender, System.EventArgs e)
{
Close();
}
private void btnCalistir_Click(object sender, System.EventArgs e)
{
Doldur();
}
private void btnGoster_Click(object sender, System.EventArgs e)
{
Bagla();
}
Yazdığımız kodlar gayet basit. Sorgumuz bir SqlDataAdapter nesnesi ile, SqlConnection'ımız
kullanılarak çalıstırılıyor ve daha sonra elde edilen veri kümesi DataSet'e aktarılıyor. Simdi
uygulamamızı bu haliyle çalıstıralım ve sorgumuzu Çalıstır baslıklı buton ile çalıstırdıktan sonra,
textBox kontrolüne mouse ile tıklayıp bir seyler yazmaya çalısalım.
Sekil 2. Đs parçacığı olmadan programın çalısması.
Görüldüğü gibi sorgu sonucu elde edilen veri kümesi DataSet'e doldurulana kadar TextBox
kontrolüne bir sey yazamadık. Çünkü islemcimiz satır kodlarını isletmek ile mesguldü ve bizim
TextBox kontrolümüze olan tıklamamızı ele almadı. Demekki buradaki sorgumuzu bir is parçacığı
içinde tanımlamalıyız. Nitekim programımız donmasın ve baska islemleride yapabilelim. Örneğin
TextBox kontrolüne bir seyler yazabilelim (bu noktada pek çok sey söylenebilir. Örneğin baska bir
tablonun güncellenmesi gibi). Bu durumda yapmamız gereken kodlamayı inanıyorumki önceki
makalelerden edindiğiniz bilgiler ile biliyorsunuzdur. Bu nedenle kodlarımızı detaylı bir sekilde
açıklamadım. Simdi gelin yeni kodlarımızı yazalım.
DataSet ds;
public void Bagla()
{
if(!t1.IsAlive)
{
dataGrid1.DataSource=ds.Tables[0];
}
}
public void Doldur()
{
SqlConnection conNorthwind=new SqlConnection("data source=localhost;initial
catalog=Northwind;integrated security=sspi");
conNorthwind.Open();
SqlDataAdapter daNorthwind=new SqlDataAdapter("SELECT Products.* From
[Order Details] Cross Join Products",conNorthwind);
ds=new DataSet();
daNorthwind.Fill(ds);
conNorthwind.Close();
MessageBox.Show("DataTable dolduruldu...");
}
ThreadStart ts1;
Thread t1;
private void btnKapat_Click(object sender, System.EventArgs e)
{
if(!t1.IsAlive)
{
Close();
}
else
{
MessageBox.Show("Is parçacigi henüz sonlandirilmadi...Daha sonra
tekrar deneyin.");
}
}
private void btnCalistir_Click(object sender, System.EventArgs e)
{
ts1=new ThreadStart(Doldur);
t1=new Thread(ts1);
t1.Start();
}
private void btnIptalEt_Click(object sender, System.EventArgs e)
{
t1.Abort();
}
private void btnGoster_Click(object sender, System.EventArgs e)
{
Bagla();
}
Simdi programımızı çalıstıralım.
Sekil 3. Đs Parçacığının sonucu.
Görüldüğü gibi bu yoğun sorgu çalısırken TextBox kontrolüne bir takım yazılar yazabildik. Üstelik
programın çalısması hiç kesilmeden. Simdi Göster baslıklı butona tıkladığımızda veri kümesinin
DataGrid kontrolüne alındığını görürüz.
Sekil 4. Programın Çalısmasının Sonucu.
Geldik bir makalemizin daha sonuna. Đlerliyen makalelerimizde Thred'leri daha derinlemesine
incelemeye devam edeceğiz. Hepinize mutlu günler dilerim.
Arayüz(Interface) Kullanımına Giris
Bugünkü makalemizde, nesneye dayalı programlamanın önemli kavramlarından birisi olan
arayüzleri incelemeye çalısacağız. Öncelikle, arayüz'ün tanımını yapalım.
Bir arayüz, baska sınıflar için bir rehberdir. Bu kısa tanımın arkasında, deryalar gibi
bir kavram denizi olduğunu söylemekte yarar buluyorum. Arayüzün ne olduğunu tam
olarak anlayabilmek için belkide asıl kullanım amacına bakmamız gerekmektedir.
C++ programlama dilinde, sınıflar arasında çok kalıtımlılık söz konusu idi. Yani bir sınıf,
kalıtımsal olarak, birden fazla sınıftan türetilebiliyordu . Ancak bu teknik bir süre sonra
kodların dahada karmasıklasmasına ve anlasılabilirliğin azalmasına neden oluyordu. Bu
sebeten ötürü değerli Microsoft mimarları, C# dilinde, bir sınıfın sadece tek bir sınıfı
kalıtımsal olarak alabileceği kısıtlmasını getirdiler. Çok kalıtımlık görevini ise
anlasılması daha kolay arayüzlere bıraktılar. Đste arayüzleri kullanmamızın en büyük
nedenlerinden birisi budur.
Diğer yandan, uygulamalarımızın geleceği açısından da arayüzlerin çok kullanıslı
olabileceğini söylememiz gerekiyor. Düsününkü, bir ekip tarafından yazılan ve gelistirilen
bir uygulamada görevlisiniz. Kullandığınız nesnelerin, türetildiği sınıflar zaman içerisinde,
gelisen yeniliklere adapte olabilmek amacıyla, sayısız yeni metoda, özelliğe vb.. sahip
olduklarını farzedin. Bir süre sonra, nesnelerin türetildiği sınıflar içerisinde yer alan
kavram kargasısını, "bu neyi yapıyordu?, kime yapıyordu? , ne için yapıyordu?" gibi
soruların ne kadar çok sorulduğunu düsünün. Oysa uygulamanızdaki sınıfların izleyeceği
yolu gösteren rehber(ler) olsa fena mı olurdu? Đste size arayüzler. Bir arayüz olusturun ve
bu arayüzü uygulayan sınıfların hangi metodları, özellikleri vb kullanması gerektiğine
karar verin. Programın gelismesimi gerekiyor? Yeni niteliklere mi ihtiyacın var? Đster
kullanılan arayüzleri, birbirlerinden kalıtımsal olarak türetin, ister yeni arayüzler
tasarlayın. Tek yapacağınız sınıfların hangi arayüzlerini kullanacağını belirtmek olucaktır.
Bu açıklamalar ısığında bir arayüz nasıl tanımlanır ve hangi üyelere sahiptir bundan
bahsedelim.Bir arayüz tanımlanması asağıdaki gibi yapılır. Yazılan kod bloğunun bir
arayüz olduğunu Interface anahtar sözcüğü belirtmektedir. Arayüz isminin basında I
harfi kullanıldığına dikkat edin. Bu kullanılan sınıfın bir arayüz olduğunu anlamamıza
yarayan bir isim kullanma tekniğidir. Bu sayede, sınıfların kalıtımsal olarak aldığı
elemanların arayüz olup olmadığını daha kolayca anlayabiliriz.
public inteface IArayuz
{
}
Tanımlama görüldüğü gibi son derece basit. Simdi arayüzlerin üyelerine bir göz atalım.
Arayüzler, sadece asağıdaki üyelere sahip olabilirler:
Arayüz Üyeleri
A. Özellikler (properties)
B. Metodlar (methods)
C. Olaylar (events)
D. Đndeksleyiciler (indexers)
Tablo 1. Arayüzlerin sahip olabileceği üyeler
Diğer yandan, arayüzler içerisinde asağıdaki üyeler kesinlikle kullanılamazlar:
Arayüzlerde Kullanılamayan
Üyeler
i. Yapıcılar (constructors)
ii. Yokediciler (destructors)
iii. Alanlar (fields)
Tablo 2. Arayüzlerde kullanılamayan üyeler.
Arayüzler Tablo1 deki üyelere sahip olabilirler. Peki bu üyeler nasıl tanımlanır. Herseyden
önce arayüzler ile ilgili en önemli kural onun bir rehber olmasıdır. Yani arayüzler sadece,
kendisini rehber alan sınıfların kullanacağı üyeleri tanımlarlar. Herhangi bir kod satırı
içermezler. Sadece özelliğin, metodun, olayın veya indeksleyicinin tanımı vardır. Onların
kolay okunabilir olmalarını sağlayan ve çoklu kalıtım için tercih edilmelerine neden olan
sebepte budur. Örneğin;
public interface IArayuz
{
/* double tipte bir özellik tanımı. get ve set anahtar
sözcüklerinin herhangibir blok {} içermediğine dikkat edin. */
double isim
{
get;
set;
}
/* Yanlız okunabilir (ReadOnly) string tipte bir özellik tanımı. */
string soyisim
{
get ;
}
/* integer değer döndüren ve ili integer parametre alan bir metod
tanımı. Metod tanımlarındada metodun dönüs tipi, parametreleri, ismi
dısında herhangibir kod satırı olmadığına dikkat edin. */
int topla(int a, int b);
/* Dönüs değeri olmayan ve herhangibir parametre almayan bir metod
tanımı. */
void yaz();
/* Bir indeksleyici tanımı */
string this [ int index]
{
get;
set;
}
}
Görüldüğü gibi sadece tanımlamalar mevcut. Herhangibir kod satırı mevcut değil. Bir
arayüz tasarlarken uymamız gereken bir takım önemli kurallar vardır. Bu kurallar
asağıdaki tabloda kısaca listelenmistir.
1
Bir arayüz'ün tüm üyeleri public kabul edilir. Private, Protected gibi belirtiçler
kullanamayız. Bunu yaptığımız takdirde örneğin bir elemanı private tanımladığımız
takdirde, derleme zamanında su hatayı alırız. "The modifier 'private' is not valid
for this item"
2
Diğer yandan bir metodu public olarakta tanımlayamayız. Çünkü zaten
varsayılan olarak bütün üyeler public tanımlanmıs kabul edilir. Bir metodu public
tanımladığımızda yine derleme zamanında su hatayı alırız. "The modifier 'public' is
not valid for this item"
3
Bir arayüz, bir yapı(struct)'dan veya bir sınıf(class)'tan kalıtımla türetilemez.
Ancak, bir arayüzü baska bir arayüzden veya arayüzlerden kalıtımsal olarak
türetebiliriz.
4 Arayüz elemanlarını static olarak tanımlayamayız.
5
Arayüzlerin uygulandığı sınıflar, arayüzde tanımlanan bütün üyeleri
kullanmak zorundadır.
Tablo 3. Uyulması gereken kurallar.
Simdi bu kadar açıklamadan sonra konuyu daha iyi anlayabilmek için basit ve açıklayıcı
bir örnek gelistirelim. Önce arayüzümüzü tasarlayalım:
public interface IArayuz
{
void EkranaYaz();
int Yas
{
get;
set;
}
string isim
{
get;
set;
}
}
Simdide bu arayüzü kullanacak sınıfımızı tasarlayalım.
public class Kisiler:IArayuz
{
}
Simdi bu anda uygulamayı derlersek, IArayuz'ündeki elemanları sınıfımız içinde
kullanmadığımızdan dolayı asağıdaki derleme zamanı hatalarını alırız.
• Interfaces1.Kisiler' does not implement interface member
'Interfaces1.IArayuz.EkranaYaz()'
• Interfaces1.Kisiler' does not implement interface member
'Interfaces1.IArayuz.isim'
• Interfaces1.Kisiler' does not implement interface member
'Interfaces1.IArayuz.Yas'
Görüldüğü gibi kullanmadığımız tüm arayüz üyeleri için bir hata mesajı olustu. Bu
noktada sunu tekrar hatırlatmak istiyorum,
Arayüzlerin uygulandığı sınıflar, arayüzde(lerde)
tanımlanan tüm üyeleri kullanmak, yani kodlamak
zorundadır.
Simdi sınıfımızı düzgün bir sekilde gelistirelim:
public class Kisiler:IArayuz /* Sınıfın kullanacağı arayüz burada
belirtiliyor.*/
{
private int y;
private string i;
/* Bir sınıfa bir arayüz uygulamamız, bu sınıfa baska üyeler
eklememizi engellemez. Burada örneğin sınıfın yapıcı metodlarınıda
düzenledik. */
public Kisiler()
{
y=18;
i="Yok";
}
/* Dikkat ederseniz özelliğin herseyi, arayüzdeki ile aynı
olmalıdır. Veri tipi, ismi vb... Bu tüm diğer arayüz üyelerinin, sınıf
içerisinde uygulanmasında da geçerlidir. */
public Kisiler(string ad,int yas)
{
y=yas;
i=ad;
}
public int Yas
{
get
{
return y;
}
set
{
y=value;
}
}
public string Isim
{
get
{
return i;
}
set
{
i=value;
}
}
public void EkranaYaz()
{
Console.WriteLine("Adım:"+i);
Console.WriteLine("Yasım:"+y);
}
}
Simdi olusturduğumuz bu sınıfı nasıl kullanacağımıza bakalım.
class Arayuz_Deneme
{
{
Kisiler kisi=new Kisiler("Burak",27);
Console.WriteLine("Yasım "+kisi.Yas.ToString());
Console.WriteLine("Adım "+kisi.Isim);
Console.WriteLine("-----------");
kisi.EkranaYaz();
}
}
Uygulamamızı çalıstırdığımızda asağıdaki sonucu elde ederiz.
Sekil 1. Uygulamanın Çalısması Sonucu.
Bu makalemizde arayüzlere kısa bir giris yaptık. Bir sonraki makalemizde ise, bir sınıfa
birden fazla arayüzün nasıl uygulanacağını inceleyeceğiz. Hepinize mutlu günler dilerim.
Temsilci(Delegate) Kavramına Giris
Bugünkü makalemizde, C# programlama dilinde ileri seviye kavramlardan biri olan
Temsilcileri(delegates) incelemeye baslayacağız. Temsilciler ileri seviye bir kavram
olmasına rağmen, her seviyden C# programcısının bilmesi gereken unsurlardandır.
Uygulamalarımızı temsilciler olmadan da gelistirebiliriz. Ancak bu durumda,
yapamıyacaklarımız, yapabileceklerimizin önüne geçecektir. Diğer yandan temsilcilerin
kullanımını gördükçe bize getireceği avantajları daha iyi anlayacağımız kanısındayım. Bu
makalemizde temsilcileri en basit haliyle anlamaya çalısıcağız.
Temsilci (delegate), program içerisinde bir veya daha fazla metodu gösteren(isaret
eden), referans türünden bir nesnedir. Programlarımızda temsilciler kullanmak
istediğimizde, öncelikle bu temsilcinin tanımını yaparız. Temsilci tanımları, arayüzlerdeki
metod tanımlamaları ile neredeyse aynıdır. Tek fark delegate anahtar sözcüğünün yer
almasıdır. Bununla birlikte, bir temsilci tanımlandığında, aslında isaret edebileceği
metod(ların) imzalarınıda belirlemis olur. Dolayısıyla, bir temsilciyi sadece tanımladığı
metod imzasına uygun metodlar için kullanabiliceğimizi söyleyebiliriz. Temsilci tanımları
tasarım zamanında yapılır. Bir temsilciyi, bir metodu isaret etmesi için kullanmak
istediğimizde ise, çalısma zamanında onu new yapılandırıcısı ile olusturur ve isaret
etmesini istediğimiz metodu ona parametre olarak veririz. Bir temsilci tanımı genel
haliyle, asağıdaki sekildeki gibidir.
Sekil 1. Temsilci tanımlaması.
Sekildende görüldüğü gibi, temsilciler aslında bir metod tanımlarlar fakat bunu
uygulamazlar. Đste bu özellikleri ile arayüzlerdeki metod tanılamalarına benzerler.
Uygulamalarımızda, temsilci nesneleri ile göstermek yani isaret etmek istediğimiz
metodlar bu imzaya sahip olmalıdır. Bildiğiniz gibi metod imzaları, metodun geri dönüs
tipi ve aldığı parametreler ile belirlenmektedir.
Bir temsilcinin tanımlanması, onu kullanmak için yeterli değildir elbette. Herseyden önce
bir amacımız olmalıdır. Bir temsilciyi çalısma zamanında olusturabiliriz ve kullanabiliriz.
Bir temsilci sadece bir tek metodu isaret edebileceği gibi, birden fazla metod için
tanımlanmıs ve olusturulmus temsilcileride kullanabiliriz. Diğer yandan, tek bir temsilcide
birden fazla temsilciyi toplayarak bu temsilcilerin isaret ettiği, tüm metodları tek bir
seferde çalıstırma lüksünede sahibizdir. Ancak temsilciler gerçek anlamda iki amaçla
kullanılırlar. Bunlardan birincisi olaylardır(events). Diğer yandan, bugünkü makalemizde
isleyeceğimiz gibi, bir metodun çalısma zamanında, hangi metodların çalıstırılacağına
karar vermesi gerektiği durumlarda kullanırız. Elbette bahsetmis olduğumuz bu amacı,
herhangibir temsilye ihtiyaç duymadan da gerçeklestirebiliriz. Ancak temsilcileri
kullanmadığımızda, bize sağladığı üstün programlama tekniği, kullanım kolaylığı ve artan
verimliliğide göz ardı etmis oluruz.
Simdi dilerseniz bahsetmis olduğumuz bu amaçla ilgili bir örnek verelim ve konuyu daha
iyi kavramaya çalısalım. Örneğin, personelimizin yapmıs olduğu satıs tutarlarına göre,
prim hesabı yapan ve ilgili yerlere bu değisiklikleri yazan bir projemiz olsun. Burada
primlerin hesaplanması için değisik katsayılar, yapılan satısın tutarına göre belirlenmis
olabilir. Örneğin bu oranlar düsük, orta ve yüksek olarak tanımlanmıs olsun. Personel
hangi gruba giriyorsa, metodumuz ona uygun metodu çağırsın. Đste bu durumda karar
verici metodumuz, çalıstırabileceği metodları temsil eden temsilci nesnelerini parametre
olarak alır. Yani, çalısma zamanında ilgili metodlar için temsilci nesneleri olusturulur ve
karar verici metoda , hangi metod çalıstırılacak ise onun temsilcisi gönderilir. Böylece
uygulamamız çalıstığında, tek yapmamız gereken hangi metodun çalıstırılması
isteniyorsa, bu metoda iliskin temsilcinin, karar verici metoda gönderilmesi olacaktır.
Oldukça karısık görünüyor. Ancak örnekleri yazdıkça daha iyi kavrayacağınıza
inanıyorum. Simdiki örneğimizde, temsilcilerin tasarım zamanında nasıl tanımlandığını,
çalısma zamanında nasıl olusturulduklarını ve karar verici bir metod için temsilcilerin nasıl
kullanılacağını incelemeye çalısacağız.
using System;
namespace Delegates1
{
public class Calistir
{
public static int a;
public delegate void temcilci(int deger); /* Temsilci tanımlamamızı yapıyoruz. Aynı
zamanda temsilcimiz , değer döndürmeyen ve integer tipte tek bir parametre alan bir
metod tanımlıyor. Temsilcimizin adı ise temsilci.*/
* Simdi bu temsilciyi kullacanak bir metod yazıyoruz. Đste karar verici metodumuz
budur. Dikkat ederseniz metodumuz parametre olarak, temsilci nesnemiz tipinden bir
temsilci(Delegate) alıyor. Daha sonra metod bloğu içinde, parametre olarak geçirilen bu
temsilcinin isaret ettiği metod çağırılıyor ve bu metoda parametre olarak integer tipte bir
değer geçiriliyor. Kısaca, metod içinden, temsilcinin isaret ettiği metod çağırılıyor.
Burada, temsilci tanımına uygun olan metodun çağırılması garanti altına alınmıstır. Yani,
programın çalısması sırasında, new yapılandırıcısı kulllanarak olusturacağımız bir
temsilci(delegate), kendi metod tanımı ile uyusmayan bir metod için yaratılmaya
çalısıldığında bir derleyici hatası alacağızdır. Dolayısıyla bu, temsilcilerin yüksek güvenlikli
isaretçiler olmasını sağlar. Bu , temsilcileri, C++ dilindeki benzeri olan isaretçilerden
ayıran en önemli özelliktir. */
public void Metod1(Calistir.temcilci t)
{
t(a);
}
}
class Class1
{
/* IkıKat ve UcKat isimli metodlarımız, temsilcimizin programın çalısması sırasında
isaret etmesini istediğimiz metodlar. Bu nedenle imzaları, temsilci tanımımızdaki metod
imzası ile aynıdır. */
public static void IkiKat(int sayi)
{
sayi=sayi*2;
Console.WriteLine("IkiKat isimli metodun temsilcisi tarafindan
çagirildi."+sayi.ToString());
}
public static void UcKat(int sayi)
{
sayi=sayi*3;
Console.WriteLine("UcKat isimli metodun temsilcisi tarafindan
çagirildi."+sayi.ToString());
}
static void Main(string[] args)
{
/* Temsilci nesnelerimiz ilgili metodlar için olusturuluyor. Burada, new
yapılandırıcısı ile olusturulan temsilci nesneleri parametre olarak, isaret edecekleri
metodun ismini alıyorlar. Bu noktadan itibaren t1 isimli delegate nesnemiz IkiKat isimli
metodu, t2 isimli delegate nesnemizde UcKat isimli metodu isaret ediceklerdir. */
Calistir.temcilci t1=new Delegates1.Calistir.temcilci(IkiKat);
Calistir.temcilci t2=new Delegates1.Calistir.temcilci(UcKat);
Console.WriteLine("1 ile 20 arası değer girin");
Calistir.a=System.Convert.ToInt32(Console.ReadLine());
Calistir c= new Calistir();
/* Kullanıcının Console penceresinden girdiği değer göre, Calistir sınıfının a
isimli integer tipteki değerini 10 ile karsılastırılıyor. 10 dan büyükse, karar verici
metodumuza t1 temsilcisi gönderiliyor. Bu durumda Metod1 isimli karar verici
metodumuz, kendi kod bloğu içinde t1 delegate nesnesinin temsil ettiği IkıKat metodunu,
Calistir.a değiskeni ile çağırıyor. Aynı islem tarzı t2 delegate nesnesi içinde geçerli.*/
if(Calistir.a>=10)
{
c.Metod1(t1);
}
else
{
c.Metod1(t2);
}
}
}
s}
Uygulamamızı çalıstıralım ve bir değer girelim.
Sekil 2. Programın çalısmasının sonucu.
Bu basit örnek ile umarım temsilciler hakkında biraz olsun bilgi sahibi olmussunuzdur.
Simdi temsilciler ile ilgili kavramlarımıza devam edelim. Yukarıdaki örneğimiz ısığında
temsilcileri programlarımızda temel olarak nasıl kullandığımızı asağıdaki sekil ile daha
kolay anlayabileceğimizi sanıyorum.
Sekil 3. Temsilcilerin Karar Verici metodlar ile kullanımı.
Yukarıdaki örneğimizde, her bir metod için tek bir temsilci tanımladık ve temsilcileri teker
teker çağırdık. Bu Single-Cast olarak adlandırılmaktadır. Ancak programlarımız da bazen,
tek bir temsilciye birden fazla temsilci ekleyerek, birden fazla metodu tek bir temsilci ile
çalıstırmak isteyebiliriz. Bu durumda Multi-Cast temsilciler tanımlarız. Simdi multi-cast
temsilciler ile ilgili bir örnek yapalım. Bu örneğimizde t1 isimli temsilcimiz, multi-cast
temsilcimiz olucak.
using System;
namespace Delegates2
{
public class temsilciler
{
public delegate void dgTemsilci(); /* Temsilcimiz tanımlanıyor. Geri dönüs değeri
olmayan ve parametre almayan metodları temsil edebilir. */
/* Metod1, Metod2 ve Metod3 temsilcilerimizin isaret etmesini istediğimiz
metodlar olucaktır.*/
public static void Metod1()
{
Console.WriteLine("Metod 1 çalıstırıldı.");
}
public static void Metod2()
{
Console.WriteLine("PI değeri 3.14 alınsın");
}
public static void Metod3()
{
Console.WriteLine("Mail gönderildi...");
}
/* Temsilcilerimizi çalıstıran metodumuz. Parametre olarak gönderilen temsilciyi,
dolayısıyla bu temsilcinin isaret ettiği metodu alıyor. */
public static void TemsilciCalistir(temsilciler.dgTemsilci dt)
{
dt(); /* Temsilcinin isaret ettiği metod çalıstırılıyor.*
}
}
class Class1
{
static void Main(string[] args)
{
/* Üç metodumuz içinde temsilci nesnelerimiz olusturuluyor .*/
temsilciler.dgTemsilci t1=new
Delegates2.temsilciler.dgTemsilci(temsilciler.Metod1);
temsilciler.dgTemsilci t2=new
Delegates2.temsilciler.dgTemsilci(temsilciler.Metod2);
temsilciler.dgTemsilci t3=new
Delegates2.temsilciler.dgTemsilci(temsilciler.Metod3);
Console.WriteLine("sadece t1");
temsilciler.TemsilciCalistir(t1);
Console.WriteLine("---");
/* Burada t1 temsilcimize, t2 temsilcisi ekleniyor. Bu durumda,
t1 temsilcimiz hem kendi metodunu hemde, t2 temsilcisinin isaret ettiği metodu isaret
etmeye baslıyor. Bu halde iken TemsilciCalistir metodumuza t1 temsilcisini göndermemiz
her iki temsilcinin isaret ettiği metodların çalıstırılmasına neden oluyor.*/
t1+=t2;
Console.WriteLine("t1 ve t2");
temsilciler.TemsilciCalistir(t1);
Console.WriteLine("---");
t1+=t3; /* Simdi t1 temsilcimiz hem t1, hem t2, hem de t3 temsilcilerinin
isaret ettiği metodları isaret etmis olucak.*/
Console.WriteLine("t1,t2 ve t3");
temsilciler.TemsilciCalistir(t1);
Console.WriteLine("---");
t1-=t2; /* Burada ise t2 metodunu t1 temsilcimizden çıkartıyoruz.
Böylece, t1 temsilcimiz sadece t1 ve t3 temsilcilerini içeriyor. */
Console.WriteLine("t1 ve t3");
temsilciler.TemsilciCalistir(t1);
Console.WriteLine("---");
}
}
}
Uygulamamızı çalıstırdığımızda asağıdaki sonucu elde ederiz.
Sekil 4. Multi-Cast temsilciler.
Geldik bir makalemizin daha sonuna. Bir sonraki makalemizde temsilcilerin kullanılıdığı
olaylar(events) kavramına gireceğiz. Hepinize mutlu günler dilerim.
Huffman Veri Sıkıstırma Algoritması ve Uygulaması
Bu makalede bilgisayar bilimlerinin önemli konularından biri olan veri sıkıstırma
algoritmalarından Huffman algoritmasını inceledikten sonra uygulamasını gerçeklestirip
sonuçlarını göreceğiz.
Sayısal haberlesme tekniklerinin önemli ölçüde arttığı günümüzde, sayısal verilen
iletilmesi ve saklanması bir hayli önem kazanmıstır. Sayısal veriler çesitli saklayıcılarda
saklanırken hedef daima minimum alanda maksimum veriyi saklamadır. Veriler çesitli
yöntemlerle sıkıstırılarak kapladığı alandan ve iletim zamanından tasarruf edilir. Sayısal
iletisim(digital communication) kuramında veriler çok çesitli yöntemlerle sıkıstırılabilir. Bu
yöntemlerden en çok bilineni David Huffman tarafından öne sürülmüstür. Bu yazıda bu
teknik "Huffman algoritması" olarak adlandırılacaktır. Bu yazıda Huffman Algoritması
detaylı olarak açıklandıktan sonra bu algoritmanın C# dili ile ne sekilde uygulanacağı
gösterilecektir.
Sıkıstırma algoritmaları temel olarak iki kategoride incelenir. Bunlar, kayıplı ve kayıpsız
sıkıstırma algoritmalarıdır. Kayıplı algoritmalarda sıkıstırılan veriden orjinal veri elde
edilemezken kayıpsız sıkıstırma algoritmalarında sıkıstırılmıs veriden orjinal veri elde
edilebilir. Kayıplı sıkıstırma tekniklerine verilebilecek en güzel örnekler MPEG ve JPEG gibi
standartlarda kullanılan sıkıstırmalardır. Bu tekniklerde sıkıstırma oranı artırıldığında
orjinal veride bozulmalar ve kayıplar görülür. Örneğin sıkıstırılmıs resim formatı olan
JPEG dosyalarının kaliteli yada az kaliteli olmasının nedeni sıkıstırma katsayısıdır. Yani
benzer iki resim dosyasından daha az disk alanı kaplayan daha kötü kalitededir deriz.
Kayıpsız veri sıkıstırmada durum çok farklıdır. Bu tekniklerde önemli olan orjinal verilerin
aynen sıkıstırılmıs veriden elde edilmesidir. Bu teknikler daha çok metin tabanlı verilen
sıkıstırılmasında kullanılır. Bir metin dosyasını sıkıstırdıktan sonra metindeki bazı
cümlelerin kaybolması istenmediği için metin sıkıstırmada bu yöntemler kullanılır.
Bu yazının konusu olan Huffman sıkıstırma algoritması kayıpsız bir veri sıkıstırma
tekniğini içerir. Bu yüzden bu yöntemin en elverisli olduğu veriler metin tabanlı verilerdir.
Bu yazıda verilecek örnek programdaki hedef metin tabanlı verilerin sıkıstırılması
olacaktır.
Huffman algoritması, bir veri kümesinde daha çok rastlanan sembolü daha düsük
uzunluktaki kodla, daha az rastlanan sembolleri daha yüksek uzunluktaki kodlarla temsil
etme mantığı üzerine kurulmustur. Bir örnekten yola çıkacak olursak : Bilgisayar
sistemlerinde her bir karakter 1 byte yani 8 bit uzunluğunda yer kaplar. Yani 10
karakterden olusan bir dosya 10 byte büyüklüğündedir. Çünkü her bir karakter 1 byte
büyüklüğündedir. Örneğimizdeki 10 karakterlik veri kümesi "aaaaaaaccs" olsun. "a"
karakteri çok fazla sayıda olmasına rağmen "s" karakteri tektir. Eğer bütün karakterleri 8
bit değilde veri kümesindeki sıklıklarına göre kodlarsak veriyi sembolize etmek için
gereken bitlerin sayısı daha az olacaktır. Söz gelimi "a" karakteri için "0" kodunu "s"
karakteri için "10" kodunu, "c" karakteri için "11" kodunu kullanabiliriz. Bu durumda 10
karakterlik verimizi temsil etmek için
(a kodundaki bit sayısı)*(verideki a sayısı) + (c kodundaki bit sayısı)*(verideki c sayısı)
+ (s kodundaki bit sayısı)*(verideki s sayısı) = 1*7 + 2*2 + 2*1 = 12 bit
gerekecektir. Halbuki bütün karakterleri 8 bit ile temsil etseydik 8*10 = 80 bite
ihtiyacımız olacaktı. Dolayısıyla %80 'in üzerinde bir sıkıstırma oranı elde etmis olduk.
Burada dikkat edilmesi gereken nokta sudur : Veri kümesindeki sembol sayısına ve
sembollerin tekrarlanma sıklıklarına bağlı olarak Huffman sıkıstırma algoritması %10 ile
%90 arasında bir sıkıstırma oranı sağlayabilir. Örneğin içinde 2000 tane "a" karakteri ve
10 tane "e" karakteri olan bir veri kümesi Huffman tekniği ile sıkıstırılırsa %90'lara varan
bir sıkıstırma oranı elde edilir.
Huffman tekniğinde semboller(karakterler) ASCII'de olduğu gibi sabit
uzunluktaki kodlarla kodlanmazlar. Her bir sembol değisken sayıda uzunluktaki
kod ile kodlanır.
Bir veri kümesini Huffman tekniği ile sıkıstırabilmek için veri kümesinde bulunan her bir
sembolün ne sıklıkta tekrarlandığını bilmemiz gerekir. Örneğin bir metin dosyasını
sıkıstırıyorsak her bir karakterin metin içerisinde kaç adet geçtiğini bilmemiz gerekiyor.
Her bir sembolün ne sıklıkta tekrarlandığını gösteren tablo frekans tablosu olarak
adlandırılmaktadır. Dolayısıyla sıkıstırma islemine geçmeden önce frekans tablosunu
çıkarmamız gerekmektedir. Bu yönteme Statik Huffman tekniği de denilmektedir. Diğer
bir teknik olan Dinamik Huffman tekniğinde sıkıstırma yapmak için frekans tablosuna
önceden ihtiyaç duyulmaz. Frekans tablosu her bir sembolle karsılastıkça dinamik olarak
olusturulur. Dinamik Huffman tekniği daha çok haberlesme kanalları gibi hangi verinin
geleceği önceden belli olmayan sistemlerde kullanılmaktadır. Bilgisayar sistemlerindeki
dosyaları sıkıstırmak için statik huffman metodu yeterlidir. Nitekim bir dosyayı bastan
sona tarayarak herbir sembolün hangi sıklıkla yer aldığını tespit edip frekans tablosunu
elde etmemiz çok basit bir islemdir.
Huffman sıkıstırma tekniğinde frekans tablosunu elde etmek için statik ve dinamik
yaklasımlarının olduğunu söyledik. Eğer statik yöntem seçilmisse iki yaklasım daha
vardır. Birinci yaklasım, metin dosyasının diline göre sabit bir frekans tablosunu
kullanmaktır. Örneğin Türkçe bir metin dosyasında "a" ve "e" harflerine çok sık
rastlanırken "ğ" harfine çok az rastlanır. Dolayısıyla "ğ" harfi daha fazla bitle "a" ve "e"
harfi daha az bitle kodlanır. Frekans tablosunu elde etmek için kullanılan diğer bir yötem
ise metni bastan sona tarayarak her bir karakterin frekansını bulmaktır. Sizde takdir
edersinizki ikinci yöntem daha gerçekçi bir çözüm üretmekle beraber metin dosyasının
dilinden bağımsız bir çözüm üretmesi ile de ön plandadır. Bu yöntemin dezavantajı ise
sıkıstırılan verilerde geçen sembollerin frekansının da bir sekilde saklanma
zorunluluğunun olmasıdır. Sıkıstırılan dosyada her bir sembolün frekansıda saklanmalıdır.
Bu da küçük boyutlu dosyalarda sıkıstırma yerine genisletme etkisi yaratabilir. Ancak bu
durum Huffman yönteminin kullanılabililiğini zedelemez. Nitekim küçük boyutlu dosyaların
sıkıstırılmaya pek fazla ihtiyacı yoktur zaten.
Frekans tablosunu metin dosyasını kullanarak elde ettikten sonra yapmamız gereken
"Huffman Ağacını" olusturmaktır. Huffman ağacı hangi karakterin hangi bitlerle temsil
edileceğini(kodlanacağını) belirlememize yarar. Birazdan örnek bir metin üzerinden
"Huffman Ağacını" teorik olarak olusturup algoritmanın derinliklerine ineceğiz. Bu örneği
iyi bir sekilde incelediğinizde Huffman algoritmasının aslında çok basit bir temel üzerine
kurulduğunu göreceksiniz.
Huffman Ağacının Olusturulması
Bir huffman ağacı asağıdaki adımlar izlenerek olusturulabilir.
Bu örnekte asağıdaki frekans tablosu kullanılacaktır.
Sembol(Karakter) Sembol Frekansı
a 50
b 35
k 20
m 10
d 8
ğ 4
Bu tablodan çıkarmamız gereken sudur : Elimizde öyle bir metin dosyası varki "a"
karakteri 50 defa, "b" karakteri 35 defa .... "ğ" karakteri 2 defa geçiyor. Amacımız ise her
bir karakteri hangi bit dizileriyle kodlayacağımızı bulmak.
1 - Öncelikle "Huffman Ağacını" ndaki en son düğümleri(dal) olusturacak bütün semboller
frekanslarına göre asağıdaki gibi küçükten büyüğe doğru sıralanırlar.
2 - En küçük frekansa sahip olan iki sembolün frekansları toplanarak yeni bir düğüm
olusturulur. Ve olusturulan bu yeni düğüm diğer varolan düğümler arasında uygun yere
yerlestirilir. Bu yerlestirme frekans bakımından küçüklük ve büyüklüğe göredir. Örneğin
yukarıdaki sekilde "ğ" ve "d" sembolleri toplanarak "12" frakansında yeni bir "ğd"
düğümü elde edilir. "12" frekanslı bir sembol sekilde "m" ve "k" sembolleri arasında
yerlestirilir. "ğ" ve "d" düğümleri ise yeni olusturulan düğümün dalları seklinde kalır. Yeni
dizimiz asağıdaki sekilde olacaktır.
3 - 2.adımdaki islem tekrarlanarak en küçük frekanslı iki düğüm tekrar toplanır ve yeni
bir düğüm olusturulur. Bu yeni düğümün frekansı 22 olacağı için "k" ve "b" düğümleri
arasına yerlesecektir. Yeni dizimiz asağıdaki sekilde olacaktır.
4 - 2.adımdaki islem tekrarlanarak en küçük frekanslı iki düğüm tekrar toplanır ve yeni
bir düğüm olusturulur. Bu yeni düğümün frekansı 42 olacağı için "b" ve "a" düğümleri
arasına yerlesecektir. Yeni dizimiz asağıdaki sekilde olacaktır. Dikkat ederseniz her dalın
en ucunda sembollerimiz bulunmaktadır. Dalların ucundaki düğümlere özel olarak
yaprak(leaf) denilmektedir. Sona yaklastıkça Bilgisayar bilimlerinde önemli bir veri
yapısı olan Tree(ağaç) veri yapısına yaklastığımızı görüyoruz.
5 - 2.adımdaki islem tekrarlanarak en küçük frekanslı iki düğüm tekrar toplanır ve yeni
bir düğüm olusturulur. Bu yeni düğümün frekansı 77 olacağı için "a" düğümünden sonra
yerlesecektir. Yeni dizimiz asağıdaki sekilde olacaktır. Dikkat ederseniz her bir düğümün
frekansı o düğümün sağ ve sol düğümlerinin frekanslarının toplamına esit olmaktadır.
Aynı durum düğüm sembolleri içinde geçerlidir.
6 - 2.adımdaki islem en tepede tek bir düğüm kalana kadar tekrar edilir. En son kalan
düğüm Huffman ağacının kök düğümü(root node) olarak adlandırılır. Son düğümün
frekansı 127 olacaktır. Böylece huffman ağacının son hali asağıdaki gibi olacaktır.
Not : Dikkat ederseniz Huffman ağacının son hali simetrik bir yapıda çıktı. Yani yaprak
düğümler hep sol tarafta çıktı. Bu tamamen seçtiğimiz sembol frekanslarına bağlı olarak
rastlantısal bir sonuçtur. Bu rastlantının olusması olusturduğumuz her bir yeni düğümün
yeni dizide ikinci sıraya yerlesmesinden kaynaklanmaktadır. Sembol frekanslarında
değisiklik yaparak ağacın seklindeki değisiklikleri kendinizde görebilirsiniz.
7 - Huffman ağacının son halini olusturduğumuza göre her bir sembolün yeni kodunu
olusturmaya geçebiliriz. Sembol kodlarını olusturuken Huffman ağacının en tepesindeki
kök düğümden baslanır. Kök düğümün sağ ve sol düğümlerine giden dala sırasıyla "0" ve
"1" kodları verilir. Sırası ters yönde de olabilir. Bu tamamen seçime bağlıdır. Ancak ilk
seçtiğiniz sırayı bir sonraki seçimlerde korumanız gerekmektedir. Bu durumda "a"
düğümüne gelen dal "0", "bkmğd" düğümüne gelen dal "1" olarak seçilir. Bu islem
ağaçtaki tüm dallar için yapılır. Dalların kodlarla isaretlenmis hali asağıdaki gibi olacaktır.
8 - Sıra geldi her bir sembolün hangi bit dizisiyle kodlanacağını bulmaya. Her bir sembol
dalların ucunda bulunduğu için ilgili yaprağa gelene kadar dallardaki kodlar birlestirilip
sembollerin kodları olusturulur. Örneğin "a" karakterine gelene kadar yalnızca "0" dizisi
ile karsılasırız. "b" karakterine gelene kadar önce "1" dizisine sonra "0" dizisi ile
karsılasırız. Dolayısıyla "b" karakterinin yeni kodu "10" olacaktır. Bu sekilde bütün
karakterlerin sembol kodları çıkarılır. Karakterlerin sembol kodları asağıda bir tablo
halinde gösterilmistir.
Frekans Sembol(Karakter) Bit Sayısı Huffman Kodu
50 a 1 0
35 b 2 10
20 k 3 110
10 m 4 1110
8 d 5 11111
4 ğ 5 11110
Sıkıstırılmıs veride artık ASCII kodları yerine Huffman kodları kullanılacaktır. Dikkat
ederseniz hiçbir Huffman kodu bir diğer Huffman kodunun ön eki durumunda değildir.
Örneğin ön eki "110" olan hiç bir Huffman kodu mevcut değildir. Aynı sekilde ön eki "0"
olan hiç bir Huffman kodu yoktur. Bu Huffman kodları ile kodlanmıs herhangi bir veri
dizisinin "tek çözülebilir bir kod" olduğunu göstermektedir. Yani sıkıstırılmıs veriden
orjinal verinin dısında baska bir veri elde etme ihtimali sıfırdır. (Tabi kodlamanın doğru
yapıldığını varsayıyoruz.)
Simdi örneğimizdeki gibi bir frekans tablosuna sahip olan metnin Huffman algoritması ile
ne oranda sıkısacağını bulalım :
Sıkıstırma öncesi gereken bit sayısını bulacak olursak : Her bir karakter esit uzunlukta
yani 8 bit ile temsil edildiğinden toplam karakter sayısı olan (50+35+20+10+8+4) = 127
ile 8 sayısını çarpmamız lazım. Orjinal veriyi sıkıstırmadan saklarsak 127*8 = 1016 bit
gerekmektedir.
Huffman algoritmasını kullanarak sıkıstırma yaparsak kaç bitlik bilgiye ihtiyaç
duyacağımızı hesaplayalım : 50 adet "a" karakteri için 50 bit, 35 adet "b" karakteri için
70 bit, 20 adet "k" karakteri için 60 bit....4 adet "ğ" karakteri için 20 bite ihtiyaç duyarız.
(yukarıdaki tabloya bakınız.) Sonuç olarak gereken toplam bit sayısı = 50*1 + 35*2 +
20*3 + 10*4 + 8*5 + 4*5 = 50 + 70 + 60 + 40 + 40 + 20 = 280 bit olacaktır.
Sonuç : 1016 bitlik ihtiyacımızı 280 bite indirdik. Yani yaklasık olarak %72 gibi bir
sıkıstırma gerçeklestirmis olduk. Gerçek bir sistemde sembol frekanslarınıda saklamak
gerektiği için sıkıstırma oranı %72'ten biraz daha az olacaktır. Bu fark genelde sıkıstırılan
veriye göre çok çok küçük olduğu için ihmal edilebilir.
Huffman Kodunun Çözülmesi
Örnekte verilen frekans tablosuna sahip bir metin içerisindeki "aabkdğmma" veri
kümesinin sıkıstırılmıs hali her karakter ile karakterin kodu yer değistirilerek asağıdaki
gibi elde edilir.
a a b k d ğ m m a
0 0 10 110 11111 11110 1110 1110 0 --> 00101101111111110111011100
Eğer elimizde frekans tablosu ve sıkıstırılmıs veri dizisi varsa islemlerin tersini yaparak
orjinal veriyi elde edebiliriz. Söyleki; sıkıstırılmıs verinin ilk biti alnır. Eğer alınan bit bir
kod sözcüğüne denk geliyorsa, ilgili kod sözcüğüne denk düsen karakter yerine koyulur,
eğer alınan bit bir kod sözcüğü değilse sonraki bit ile birlikte ele alınır ve yeni dizinin bir
kod sözcüğü olup olmadığına bakılır. Bu islem dizinin sonuna kadar yapılır ve huffman
kodu çözülür. Huffman kodları tek çözülebilir kod olduğu için bir kod dizisinden farklı
semboller elde etmek olanaksızdır. Yani bir huffman kodu ancak ve ancak bir sekilde
çözülebilir. Bu da aslında yazının basında belirtilen kayıpsız sıkıstırmanın bir sonucudur.
C# ile Huffman Algoritmasının Gerçeklestirilmesi
Huffman algoritması bilgisayar bilimlerinde genellikle metin dosyalarının sıkıstırılmasında
kullanılır. Bu yazıda örnek bir program ile Huffman algoritmasının ne sekilde
uygulanacabileceğini de göreceğiz. Dil olarak tabiki C#'ı kullanılacaktır.
Huffman algoritmasının uygulanmasındaki ilk adım frekans tablosunun bulunmasıdır. Bu
yüzden bir dosyayı sıkıstırmadan önce dosyadaki her bir karakterin ne sıklıkla yer aldığını
bulmak gerekir. Örnek programda sembol frekanslarını bellekte tutmak için
System.Collections isim alanıdan bulunan Hashtable koleksiyon yapısı kullanılmıstır.
Örneğin "c" karakterinin frekansı 47 ise,
CharFrequencies["c"] = 47
seklinde sembolize edilir. Örnek programda frekans tablosu asağıdaki metot ile elde
edilebilir. Sıkıstırılacak dosya bastan sona taranarak frekanslar hashtable nesnesine
yerlestirilir.
[Not : Örnek uygulamada kullandığım değisken ve metot isimlerini bazı özel nedenlerden
dolayı Đngilizce yazmak zorunda kaldığım için tüm okurlardan özür dilerim.]
private Hashtable CharFrequencies = new Hashtable();
private void MakeCharFrequencies()
{
FileStream fs = File.Open(file,FileMode.Open,FileAccess.Read);
StreamReader sr = new StreamReader(fs,System.Text.Encoding.ASCII);
int iChar;
char ch;
while((iChar = sr.Read()) != -1)
{
ch = (char)iChar;
if(!CharFrequencies.ContainsKey(ch.ToString()))
{
CharFrequencies.Add(ch.ToString(),1);
}
else
{
int oldFreq = (int)CharFrequencies[ch.ToString()];
int newFreq;
newFreq = oldFreq + 1;
CharFrequencies[ch.ToString()] = newFreq;
}
}
sr.Close();
fs.Close();
sr = null;
fs = null;
}
Frekans tablosunu yukarıdaki metot yardımıyla olusturduktan sonra huffman
algoritmasının en önemli adımı olan huffman ağacının olusturulmasına sıra geldi. Huffman
ağacının olusturulması en önemli ve en kritik noktadır. Huffman ağacı olusturulurken
Tree(ağaç) veri yapısından faydalanılmıstır. Ağaçtaki her bir düğümü temsil etmek için bir
sınıf yazılmıstır. Huffman ağacındaki düğümleri temsil etmek için kullanılabilecek en basit
düğüm sınıfının yapısı asağıdaki gibidir.
Yukarıdaki düğüm yapısı algoritmayı uygulamak için gereken en temel düğüm yapısıdır.
Bu yapı istenildiği gibi genisletilebilir. Önemli olan minimum gerekliliği sağlamaktır.
Düğüm sınıfındaki SagDüğüm ve SolDüğüm özellikleri de düğüm türündendir.
Baslangıçtaki düğümler için bu değerler null dır. Her bir yeni düğümm olusturulduğunda
yeni düğümün sağ ve sol düğümleri olusur. Ancak unutmamak gerekir ki sadece
sembollerimizi olusturan yaprak düğümlerde bu özellikler null değerdedir.
Örnek uygulamadaki HuffmanNode sınıfında ayrıca ilgili düğümün yaprak olup
olmadığını gösteren IsLeaf ve ilgili düğümün ana(parent) düğümünü gösteren
HuffmanNode türünden ParentNode özellikleri bulunmaktadır.
HuffmanNode düğüm sınıfının yapısı asağıdaki gibidir.
using System;
namespace Huffman
{
public class HuffmanNode
{
private HuffmanNode leftNode;
private HuffmanNode rightNode;
private HuffmanNode parentNode;
private string symbol;
private int frequency;
private string code = "";
private bool isLeaf;
public HuffmanNode LeftNode
{
get{return leftNode;}
set{leftNode = value;}
}
public HuffmanNode RightNode
{
get{return rightNode;}
set{rightNode = value;}
}
public HuffmanNode ParentNode
{
get{return parentNode;}
set{parentNode = value;}
}
public string Symbol
{
get{return symbol;}
set{symbol = value;}
}
public string Code
{
get{return code;}
set{code = value;}
}
public int Frequency
{
get{return frequency;}
set{frequency = value;}
}
public bool IsLeaf
{
get{return isLeaf;}
set{isLeaf = value;}
}
public HuffmanNode()
{
}
}
public class NodeComparer : IComparer
{
public NodeComparer()
{
}
public int Compare(object x, object y)
{
HuffmanNode node1 = (HuffmanNode)x;
HuffmanNode node2 = (HuffmanNode)y;
return node1.Frequency.CompareTo(node2.Frequency);
}
}
}
Baslangıçta sembol sayısı kadar HuffmanNode nesnesi yani huffman düğümü
olusturmamız gerekecektir. Olusturulan bu düğümler Arraylist koleksiyon yapısında
tutulmaktadır. Her bir yeni düğüm olusturulduğunda Arraylist kolaksiyonun yeni bir
düğüm eklenir ve iki düğüm çıkarılır. Yeni düğümün sağ ve sol düğümleri çıkarılan
düğümler olacaktır. Đlk olusturulan düğümlerin sağ ve sol düğümlerinin null olduğunu
hatırlayınız. Yeni düğüm eklendiğinde Arraylist sınıfının Sort() metodu yardımıyla
düğümler frekanslarına göre küçükten büyüğe doğru sıralanır. Yani Arraylist elemanını ilk
elemanı en küçük frekanslı düğümdür. Sort() metodu IComparer arayüzünü kullanan
NodeComparer sınıfını kullanmaktadır. Yani bu sınıftaki Compare() metodu yukarıdaki
kodda belirtildiği gibi düğüm nesnelerinin neye göre sıralancağını tanımlar.
Yukarıdaki tanımlar ısığında Huffman ağacı asağıdaki metot yardımıyla bulunabilir. Dikkat
ettiyseniz, metodun icrası bittiğinde Nodes koleksiyonunda tek bir düğüm nesnesi
kalacaktır, bu da huffman ağacındaki kök düğümden(root node) baskası değildir.
private ArrayList Nodes;
public void MakeHuffmanTree()
{
Nodes = new ArrayList();
//Baslangıç düğümleri olusturuluyor.
foreach(Object Key in CharFrequencies.Keys)
{
HuffmanNode node = new HuffmanNode();
node.LeftNode = null;
node.RightNode = null;
node.Frequency = (int)(CharFrequencies[Key]);
node.Symbol = (string)(Key);
node.IsLeaf = true;
node.ParentNode = null;
Nodes.Add(node);
}
//Düğümlerin neye göre sıralanacağı NodeComparer sınıfındaki Compare metodunda
tanımlıdır.
Nodes.Sort(new NodeComparer());
while(Nodes.Count > 1)
{
HuffmanNode node1 = (HuffmanNode)Nodes[0];
HuffmanNode node2 = (HuffmanNode)Nodes[1];
HuffmanNode newnode1 = new HuffmanNode();
newnode1.Frequency = node1.Frequency + node2.Frequency;
newnode1.IsLeaf = false;
newnode1.LeftNode = node1;
newnode1.RightNode = node2;
newnode1.Symbol = node1.Symbol + node2.Symbol;
node1.ParentNode = newnode1;
node2.ParentNode = newnode1;
Nodes.Add(newnode1);
Nodes.Remove(node1);
Nodes.Remove(node2);
Nodes.Sort(new NodeComparer());
}
}
Algoritmanın son adımı ise Huffman kodlarının olusturulmasıdır. Programatik olarak
kodlamanın en zor olduğu bölüm burasıdır. Zira burada huffman ağacı özyineli(
recursive) olarak taranarak her bir düğümün huffman kodu atanacaktır.
Performansı artırmak için semboller ve kod sözcükleri ayrıca bir Hashtable
koleksiyonunda saklanmıstır. Böylece her bir kodun çözülmesi sırasında ağaç yapısı
taranmayacak, bunun yerine Hashtable'dan ilgili kod bulunacaktır. Kodlamayı yapan
metot asağıdaki gibidir.
private void FindCodeWords(HuffmanNode Node)
{
HuffmanNode leftNode = Node.LeftNode;
HuffmanNode rightNode = Node.RightNode;
if(leftNode == null && rightNode == null)
Node.Code = "1";
if(Node == this.RootHuffmanNode)
{
if(leftNode != null)
leftNode.Code = "0";
if(rightNode != null)
rightNode.Code= "1";
}
else
{
if(leftNode != null)
leftNode.Code = leftNode.ParentNode.Code + "0";
if(rightNode != null)
rightNode.Code= rightNode.ParentNode.Code + "1";
}
if(leftNode != null)
{
if(!leftNode.IsLeaf)
FindCodeWords(leftNode);
else
{
CodeWords.Add(leftNode.Symbol[0].ToString(),leftNode.Code);
}
}
if(rightNode != null)
{
if(!rightNode.IsLeaf)
FindCodeWords(rightNode);
else
{
CodeWords.Add(rightNode.Symbol[0].ToString(),rightNode.Code);
}
}
}
Huffman algoritmasının temel basamakları bitmis durumda. Bundan sonraki kodlar elde
edilen sıkıstırılmıs verinin ne sekilde dosyaya kaydedileceği ve sıkıstırılmıs dosyanın nasıl
tekrar geri elde edileceği ile ilgilidir. Bu noktada uygulamamız için bir dosya formatı
belirlememiz gerekmektedir. Hatırlanacağı üzere sıkıstırılmıs veriyi tekrar eski haline
getirmek için frekans tablosuna ihtiyacımız vardır. Dolayısıyla sıkıstırılmıs veriyi dosyaya
yazmadan önce dosya formatımızın kurallarına göre sembol frekanslarını dosyaya
yazmamız gerekir.
Benim programı yazarken gelistirdiğim dosya formatı asağıdaki gibidir.
dosya.huff
-----------
1- ) Đlk 4 byte --> Orjinal verideki toplam sembol sayısı.(ilk byte yüksek anlamlı byte
olacak sekilde)
2 -) Sonraki 1 byte --> Data bölümünde bulunan son byte'taki ilk kaç bitin data'ya dahil
olduğunu belirtir. (Data bölümündeki bitlerin sayısı 8'in katı olmayabilir.)
3 -) 2 byte Sembol + 4 byte sembol frekansı (bu bölüm ilk 4 byte'ta belirtilen sembol
sayısı kadar tekrar edecektir.-ilk byte yüksek anlamlı byte olacak sekilde-)
4 -) Son bölümde ise sıkıstırılmıs veri bit dizisi seklinde yer alır.
Asağıdaki metot yukarıda belirlenen kurallara göre sıkıstırılmıs veriyi dosyaya .huff
uzantılı olarak kaydedir.
[Not : Yazılacak programa göre bu dosya formatı değisebilir. Örneğin birden fazla dosyayı
aynı anda sıkıstıran bir program için bu format tamamen değisecektir.]
public void WriteCompressedData()
{
/// 4 byte ->(uint) Sembol Sayısı(K adet)
/// 1 byte ->(uint) Data bölümünde bulunan son byte'taki ilk kaç bitin data'ya dahil
olduğunu belirtir.
///
/// ------------------SEMBOL FREKANS BÖLÜMÜ------------------------------------------
///
/// 2 byte Sembol + 4 byte Sembol Frekansı
/// .
/// . K adet
/// .
/// 2 byte Sembol + 4 byte Sembol Frekansı
///
///
/// ------------------DATA BÖLÜMÜ----------------------------------------------------
///
/// Bu bölümde Sıkıstırılmıs veriler byte dizisi seklinde yer alır.
///
/// Data bölümü (6*K + 6) numaralı byte'tan itibaren baslar.
///
int K = CharFrequencies.Keys.Count;
/*K = 4 ise
*
* byte[0] = 0 0 0 0 0 1 0 0
* byte[1] = 0 0 0 0 0 0 0 0
* byte[2] = 0 0 0 0 0 0 0 0
* byte[3] = 0 0 0 0 0 0 0 0
*
* K(bits) = 00000100 00000000 00000000 00000000
* */
byte[] byteK = BitConverter.GetBytes(K);
FileStream fs = File.Open(this.NewFile,FileMode.Create,FileAccess.ReadWrite);
byte[] CompressedByteStream = GenerateCompressedByteStream();
WriteByteArrayToStream(byteK,fs);
fs.WriteByte(RemainderBits);
foreach(string sembol in CharFrequencies.Keys)
{
byte[] byteC = BitConverter.GetBytes((char)sembol[0]);
byte[] bytesFreqs = BitConverter.GetBytes((int)CharFrequencies[sembol]);
WriteByteArrayToStream(byteC,fs);
WriteByteArrayToStream(bytesFreqs,fs);
}
WriteByteArrayToStream(CompressedByteStream,fs);
fs.Flush();
fs.Close();
}
Sıkıstırılan bir veri geri elde edilemediği sürece sıkıstırmanın bir anlam ifade etmeyeceği
düsünülürse sıkıstırma isleminin tersinide yazmamız gerekmektedir. Sıkıstırma islemi
yaparken yaptığımız islemlerin tersini yaparak orjinal veriyi elde edebiliriz. Sıkıstırılmıs bir
dosyayı açmak için asağıdaki adımlar izlenir.
1 -) .huff uzantılı dosyadan sembol frekansları okunur ve Hashtable nesnesine
yerlestirilir.
2 -) Frekans tablosu kullanılarak Huffman ağacı olusturulur.
3 -) Huffman ağacı kullanılarak Huffman kodları olusturulur.
4 -) .huff uzantılı dosyanın data bölümünden sıkıstırılmıs veri dizisi okunarak huffman
kodları ile karsılastırılarak orjinal metin bulunur ve .txt uzantılı dosyaya yazılır.
2. ve 3. adımlardaki islemler sıkıstırma yapıldığında kullanılan islemler ile aynıdır. Burada
ayrıca değenmeye gerek yoktur. Bu asamada önemli olan verilen okunması ve
yazılmasında izlenen yoldur. Verilerin okunmasında ve tekrar yazılmasında
StringBuilder sınıfı kullanılmıstır. Bu sınıf string nesnesine göre oldukça iyi performans
göstermektedir. Örneğin aynı islemi string ile yaptığımda 75 K'lık bir dosya açma islemi
30 sn sürerken StringBuilder kullandığımda 2 sn sürmektedir.
1.asamdaki islemi yapacak metot asağıdaki gibidir.
private System.Text.StringBuilder CompressedData = new
System.Text.StringBuilder("");
private void ReadCompressedFile()
{
FileStream fs = File.Open(file,FileMode.Open,FileAccess.Read);
//Đlk önce sembol sayısını bulalım.
byte[] byteK = new byte[4];
for(int i=0 ; i<4 ; i++)
{
byteK[i] = (byte)fs.ReadByte();
}
int SymbolCount = BitConverter.ToInt32(byteK,0);
int RemainderBitCount = (byte)fs.ReadByte();
for(int k = 0; k < SymbolCount ; k++)
{
//Sembollerin elde edilmesi(2 byte)
byte[] symbolB = new byte[2];
symbolB[0] = (byte)fs.ReadByte();
symbolB[1] = (byte)fs.ReadByte();
char cSymbol = BitConverter.ToChar(symbolB,0);
//Frekans bilgisinin elde edilmesi(4 byte)
byte[] freqB = new byte[4];
for(int j=0 ; j<4 ; j++)
{
freqB[j] = (byte)fs.ReadByte();
}
int freq = BitConverter.ToInt32(freqB,0);
CharFrequencies.Add(cSymbol.ToString(),freq);
}
//Data bölümünün okunması
int readByte;
while((readByte = fs.ReadByte()) != -1)
{
byte b = (byte)readByte;
bool[] bits = new bool[8];
bits = GetBitsOfByte(b);
BitArray ba = new BitArray(bits);
for(int m=0 ; m < ba.Length ; m++)
{
if(ba[m])
CompressedData.Append("1");
else
CompressedData.Append("0");
}
}
//Son byte'taki fazla veriler atılıyor
int removingBits = 8 - RemainderBitCount;
if(RemainderBitCount != 0)
{
CompressedData.Remove(CompressedData.Length - removingBits, removingBits);
}
fs.Flush();
fs.Close();
}
Dosyayı açarken son yapılması gereken adım okunan verilerin dosyaya yazılmasıdır. Bir
önceki adımda okunan veriler asağıdaki metot aracılığıyla yeni bir dosyaya kaydedilir.
System.Text.StringBuilder OriginalData = new System.Text.StringBuilder("");
private void WriteOrginalData()
{
FileStream fs = File.Open(this.NewFile,FileMode.Create,FileAccess.ReadWrite);
StreamWriter sw = new StreamWriter(fs,System.Text.Encoding.ASCII);
System.Text.StringBuilder sb = new System.Text.StringBuilder("");
//Hashtable'ın performansından faydalanmak için kod sözcükleri ters çevrilip yeni bir
hashtable olusturuluyor.
ReverseCodeWordsHashtable();
for(int i=0 ; i < CompressedData.Length ; i++)
{
sb.Append(CompressedData[i]);
string symbol = "";
bool found = false;
if(CodeWords.ContainsValue(sb.ToString()))
{
symbol = (string)ReversedCodeWords[sb.ToString()];
found = true;
}
if(found)
{
OriginalData.Append(symbol);
sb.Remove(0,sb.Length);
}
}
sw.Write(OriginalData.ToString());
sw.Flush();
fs.Flush();
sw.Close();
fs.Close();
}
Yukarıdaki metotların tamamı Huffman isimli bir metotta toplanmıstır. Dosya sıkıstırma
ve dosya açma için farklı nesnelerin kullanılması gerekmektedir. Huffman sınıfının örnek
kullanımı asağıda verilmistir.
//Sıkıstırma
Huffman hf1 = new Huffman("deneme.txt");
hf1.Compress();
//Açma
Huffman hf2 = new Huffman("deneme.huff");
hf2.Decompress();
Uygulamayı 75 K'lık bir Đngilizce metin tabanlı dosya üzerinde çalıstırdığımda dosyanın 43
K'ya düstüğünü asağıdaki gibi gözlemledim.
Umarım faydalı bir prgram ve faydalı bir yazı olmustur.
Uygulamanın bütün kodlarını ve proje dosyasını indirmek için tıklayınız.
Not : Bu makalenin son cümlesini yazdığımda programın açma modülünde Türkçe
karakterler ile ilgili bir bug'ın olduğunu farkettim. Yani su anda herhangi bir türkçe
karakter içeren dosyayı sıkıstırıp dosyayı tekrar açtığınızda Türkçe karakterler yerine "?"
karakterini görecekseniz. En kısa zamanda bug'ı düzeltip kaynak kodları yeniden
yükleyeceğim. Bug'ın nedenini bulup düzelten kisiye süpriz bir ödül vereceğimide
belirtmek isterim.
Bir Arayüz, Bir Sınıf ve Bir Tablo
Bugünkü makalemizde, bir arayüzü uygulayan sınıf nesnelerinden faydalanarak, bir Sql
tablosundan nasıl veri okuyacağımızı ve değisiklikleri veritabanına nasıl göndereceğimizi
incelemeye çalısacağız. Gelistireceğimiz örnek, arayüzlerin nasıl olusturulduğu ve bir
sınıfa nasıl uygulandığını incelemekle yetinmeyecek, Sql veritabanımızdaki bir tablodaki
belli bir kayda ait verilerin bu sınıf nesnelerine nasıl aktarılacağını da isleyecek. Kısacası
uygulamamız, hem arayüzlerin hem sınıfların hemde Sql nesnelerinin kısa bir tekrarı
olucak.
Öncelikle uygulamamızın amacından bahsedelim. Uygulamamızı bir Windows uygulaması
seklinde gelistireceğiz. Kullanacağımız veri tablosunda arkadaslarımızla ilgili bir kaç veriyi
tutuyor olacağız. Kullanıcı, Windows formunda, bu tablodaki alanlar için Primary Key
niteliği tasıyan bir ID değerini girerek, buna karsılık gelen tablo satırına ait verilerini elde
edicek. Đstediği değisiklikleri yaptıktan sonra ise bu değisiklikleri tekrar veritabanına
gönderecek. Burada kullanacağımız teknik makalemizin esas amacı olucak. Bu kez veri
tablosundan çekip aldığımız veri satırının programdaki esdeğeri, olusturacağımız sınıf
nesnesi olucak. Bu sınıfımız ise, yazmıs olduğumuz arayüzü uygulayan bir sınıf olucak.
Veriler sınıf nesnesine, satırdaki her bir alan değeri, aynı isimli özelliğe denk gelicek
sekilde yüklenecek. Yapılan değisiklikler yine bu sınıf nesnesinin özelliklerinin sahip
olduğu değerlerin veri tablosuna gönderilmesi ile gerçeklestirilecek.
Uygulamamızda, verileri Sql veritabanından çekmek için, SqlClient isim uzayında yer alan
SqlConnection ve SqlDataReader nesnelerini kullanacağız. Hatırlayacağınız gibi
SqlConnection nesnesi ile , bağlanmak istediğimiz veritabanına, bu veritabanının
bulunduğu sunucu üzerinden bir bağlantı tanımlıyoruz. SqlDataReader nesnemiz ile de,
sadece ileri yönlü ve yanlız okunabilir bir veri akımı sağlayarak, aradığımız kayda ait
verilerin elde edilmesini sağlıyoruz. Simdi uygulamamızı gelistirmeye baslayalım.
Öncelikle vs.net ortamında bir Windows Application olusturalım. Burada asağıdaki gibi bir
form tasarlayalım.
Sekil 1. Form tasarımımız.
Kullanıcı bilgilerini edinmek istediği kisinin ID’nosunu girdikten sonra, Getir baslıklı
butona tıklayarak ilgili satırın tüm alanlarına ait verileri getirecek. Ayrıca, kullanıcı veriler
üzerinde değisiklik yapabilecek ve bunlarıda Güncelle baslıklı butona tıklayarak Sql
veritabanındaki tablomuza aktarabilecek. Sql veritabanında yer alan Kisiler isimli
tablomuzun yapısı asağıdaki gibidir.
Sekil 2. Tablomuzun yapısı.
Simdi gelelim isin en önemli ve anahtar kısımlarına. Program kodlarımız. Öncelikle
arayüzümüzü tasarlayalım. Arayüzümüz, sonra olusturacağımız sınıf için bir rehber
olucak. Sınıfımız, veri tablomuzdaki alanları birer özellik olarak tasıyacağına göre
arayüzümüzde bu özellik tanımlarının yer alması gerektiğini söyleyebiliriz. Ayrıca ilgili
kisiye ait verileri getirecek bir metodumuzda olmalıdır. Elbette bu arayüze baska amaçlar
için üye tanımlamalarıda ekleyebiliriz. Bu konuda tek sınır bizim hayal gücümüz. Đsin
gerçeği bu makalemizde hayal gücümü biraz kısdım konunun daha fazla dağılmaması
amacıyla :) . (Ama siz, örneğin kullanıcının yeni girdiği verileri veritabanına yazıcak bir
metod tanımınıda bir üye olarak ekleyebilir ve gerekli kodlamaları yapabilirsiniz.) Đste
arayüzümüzün kodları.
public interface IKisi
{
/* Öncelikle tablomuzdaki her alana karsılık gelen özellikler için tanımlamalarımızı
yapıyoruz.*/
int KisiID /* KisiID, tablomuzda otomatik artan ve primary key olan bir alandır.
Dolayısıyla programcının var olan bir KisiID’sini değistirmemesi gerekir. Bu nedenle
sadece okunabilir bir özellik olarak tanımlanmasına izin veriyoruz. */
{
get;
}
string Ad /* Tablomuzdaki char tipindeki Ad alanımız için string tipte bir alan.*/
{
get;set;
}
string Soyad
{
get;set;
}
DateTime DogumTarihi /* Tablomuzda, DogumTarihi alanımız datetime tipinde
olduğundan, DateTime tipinde bir özellik tanımlanmasına izin veriyoruz.*/
{
get;set;
}
string Meslek
{
get;set;
}
void Bul(int KID); /* Bul metod, KID parametresine göre, tablodan ilgili satıra ait
verileri alıcak ve alanlara karsılık gelen özelliklere atayacak metodumuzdur.*/
}
Simdide bu arayüzümüzü uygulayacağımız sınıfımızı olusturalım. Sınıfımız IKisi
arayüzünde tanımlanan her üyeyi uygulamak zorundadır. Bu bildiğiniz gibi arayüzlerin bir
özelliğidir.
public class CKisi:IKisi /* IKisi arayüzünü uyguluyoruz.*/
{
/* Öncelikle sınıftaki özelliklerimiz için, verilerin tutulacağı alanları tanımlıyoruz.*/
private int kisiID;
private string ad;
private string soyad;
private DateTime dogumTarihi;
private string meslek;
/* Arayüzümüzde yer alan üyeleri uygulamaya baslıyoruz.*/
public int KisiID
{
get{ return kisiID;}
}
public string Ad
{
get{return ad;}set{ad=value;}
}
public string Soyad
{
get{return soyad;}set{soyad=value;}
}
public DateTime DogumTarihi
{
get{return dogumTarihi;}set{dogumTarihi=value;}
}
public string Meslek
{
get{return meslek;}set{meslek=value;}
}
public void Bul(int KID)
{
/* Öncelikle Sql Veritabanımıza bir bağlantı açıyoruz.*/
SqlConnection conFriends=new SqlConnection("data source=localhost;integrated
security=sspi;initial catalog=Friends");
/* Tablomuzdan, kullanıcının bu metoda parametre olarak gönderdiği KID değerini
baz alarak, ilgili KisiID’ye ait verileri elde edicek sql kodunu yazıyoruz.*/
string sorgu="Select * From Kisiler Where KisiID="+KID.ToString();
/* SqlCommand nesnemiz yardımıyla sql sorgumuzu çalıstırılmak üzere
hazırlıyoruz.*/
SqlCommand cmd=new SqlCommand(sorgu,conFriends);
SqlDataReader rd;/* SqlDataReader nesnemizi yaratıyoruz.*/
conFriends.Open(); /* Bağlantımızı açıyoruz. */
rd=cmd.ExecuteReader(CommandBehavior.CloseConnection); /* ExecuteReader
ile sql sorgumuzu çalıstırıyoruz ve sonuç kümesi ile SqlDataReader nesnemiz arasında bir
akım(stream) açıyoruz. CommandBehavior.CloseConnection sayesinde, SqlDataReader
nesnemizi kapattığımızda, SqlConnection nesnemizinde otomatik olarak kapanmasını
sağlıyoruz.*/
while(rd.Read())
{
/* Eğer ilgili KisiID’ye ait bir veri satırı bulunursa, SqlDataReader nesnemizin
Read metodu sayesinde, bu satıra ait verileri sınıfımızın ilgili alanlarına aktarıyoruz.
Böylece, bu alanların atandığı sınıf özellikleride bu veriler ile dolmus oluyor.*/
kisiID=(int)rd["KisiID"];
ad=rd["Ad"].ToString();
soyad=rd["Soyad"].ToString();
dogumTarihi=(DateTime)rd["DogumTarihi"];
meslek=rd["Meslek"].ToString();
}
rd.Close();
}
public CKisi()
{
}
}
Artık IKisi arayüzünü uygulayan, CKisi isimli bir sınıfımız var.Simdi Formumuzun kodlarını
yazmaya baslayabiliriz. Öncelikle module düzeyinde bir CKisi sınıf nesnesi tanımlayalım.
CKisi kisi=new CKisi();
Bu nesnemiz veri tablosundan çektiğimiz veri satırına ait verileri tasıyacak. Kullanıcı Getir
baslıklı button kontrolüne bastığında olucak olayları gerçeklestirecek kodları yazalım.
private void btnGetir_Click(object sender, System.EventArgs e)
{
int id=Convert.ToInt32(txtKisiID.Text.ToString()); /* Kullanıcının TextBox kontrolüne
girdiği ID değeri Convert sınıfının ToInt32 metodu ile Integer’a çeviriyoruz.*/
kisi.Bul(id); /* Kisi isimli CKisi sınıfından nesne örneğimizin Bul metodunu
çağırıyoruz.*/
Doldur(); /* Doldur Metodu, kisi nesnesinin özellik değerlerini, Formumuzdaki ilgili
kontrollere alarak, bir nevi veri bağlama islemini gerçeklestirmis oluyor.*/
}
Simdide Doldur metodumuzun kodlarını yazalım.
public void Doldur()
{
txtAd.Text=kisi.Ad.ToString(); /* txtAd kontrolüne, kisi nesnemizin Ad özelliğinin su
anki değeri yükleniyor. Yani ilgili veri satırının ilgili alanı bu kontrole bağlamıs oluyor.*/
txtSoyad.Text=kisi.Soyad.ToString();
txtMeslek.Text=kisi.Meslek.ToString();
txtDogumTarihi.Text=kisi.DogumTarihi.ToShortDateString();
lblKisiID.Text=kisi.KisiID.ToString();
}
Evet görüldüğü gibi artık aradığımız kisiye ait verileri formumuzdaki kontrollere
yükleyebiliyoruz. Simdi TextBox kontrollerimizin TextChanged olaylarını kodlayacağız.
Burada amacımız, TextBox’larda meydana gelen değisikliklerin anında, CKisi sınıfından
türettiğimiz Kisi nesnesinin ilgili özelliklerine yansıtılabilmesi. Böylece yapılan değisiklikler
anında nesnemize yansıyacak. Bu nedenle asağıdaki kodları ekliyoruz.
/* Metodumuz bir switch case ifadesi ile, aldığı ozellikAdi parametresine göre, CKisi isimli
sınıfımıza ait Kisi nesne örneğinin ilgili özelliklerini değistiriyor.*/
public void Degistir(string ozellikAdi,string veri)
{
switch(ozellikAdi)
{
case "Ad":
{
kisi.Ad=veri;
break;
}
case "Soyad":
{
kisi.Soyad=veri;
break;
}
case "Meslek":
{
kisi.Meslek=veri;
break;
}
case "DogumTarihi":
{
kisi.DogumTarihi=Convert.ToDateTime(veri);
break;
}
}
}
private void txtAd_TextChanged(object sender, System.EventArgs e)
{
Degistir("Ad",txtAd.Text);
}
private void txtSoyad_TextChanged(object sender, System.EventArgs e)
{
Degistir("Soyad",txtSoyad.Text);
}
private void txtDogumTarihi_TextChanged(object sender, System.EventArgs e)
{
Degistir("DogumTarihi",txtDogumTarihi.Text.ToString());
}
private void txtMeslek_TextChanged(object sender, System.EventArgs e)
{
Degistir("Meslek",txtMeslek.Text);
}
private void btnGuncelle_Click(object sender, System.EventArgs e)
{
int id;
id=Convert.ToInt32(lblKisiID.Text.ToString());
Guncelle(id);
}
Görüldüğü gibi kodlarımız gayet basit. Simdi güncelleme islemlerimizi
gerçeklestireceğimiz kodları yazalım. Kullanıcımız, TextBox kontrollerinde yaptığı
değisikliklerin veritabanınada yansıtılmasını istiyorsa Guncelle baslıklı button kontrolüne
tıklayacaktır. Đste kodlarımız.
private void btnGuncelle_Click(object sender, System.EventArgs e)
{
/* Güncelleme islemi, su anda ekranda olan Kisi için yapılacağından, bu kisiye ait
KisiID sini ilgili Label konrolümüzden alıyoruz ve Guncelle isimli metodumuza parametre
olarak gönderiyoruz. Asıl güncelleme islemi Guncelle isimli metodumuzda yapılıyor. */
int id;
id=Convert.ToInt32(lblKisiID.Text.ToString());
Guncelle(id);
}
public void Guncelle(int ID)
{
/* Sql Server’ımıza bağlantımızı olusturuyoruz.*/
SqlConnection conFriends=new SqlConnection("data source=localhost;integrated
security=sspi;initial catalog=Friends");
/* Update sorgumuzu olusturuyoruz. Dikkat edicek olursanız alanlara atanacak
değerler, kisi isimli nesnemizin özelliklerinin değerleridir. Bu özellik değerleri ise, TextBox
kontrollerinin TextChanged olaylarına ekldeğimiz kodlar ile sürekli güncel tutulmaktadır.
En ufak bir değisiklik dahi buraya yansıyabilecektir.*/
string sorgu="Update Kisiler Set
Ad=’"+kisi.Ad+"’,Soyad=’"+kisi.Soyad+"’,Meslek=’"+kisi.Meslek+"’,DogumTarihi=’"+kisi
.DogumTarihi.ToShortDateString()+"’ Where KisiID="+ID;
SqlCommand cmd=new SqlCommand(sorgu,conFriends); /* SqlCommand nesnemizi
sql cümleciğimiz ve geçerli bağlantımız ile olusturuyoruz. */
conFriends.Open(); /* Bağlantımızı açıyoruz.*/
try
{
cmd.ExecuteNonQuery(); /* Komutumuzu çalıstırıyoruz.*/
}
catch
{
MessageBox.Show("Basarısız");
}
finally /* Update islemi herhangibir neden ile basarısız olsada, olmasada sonuç
olarak(finally) açık olan SqlConnection bağlanıtımızı kapatıyoruz. */
{
conFriends.Close();
}
}
Đste uygulama kodlarımız bu kadar. Simdi gelin uygulamamızı çalıstırıp deneyelim.
Öncelikle KisiID değeri 1000 olan satıra ait verileri getirelim.
Sekil 3. KisiID=1000 Kaydına ait veriler Kisi nesnemize yüklenir.
Simdi verilerde bir kaç değisiklik yapalım ve güncelleyelim. Ben Ad alanında yer alan "S."
değerini "Selim" olarak değistirdim. Bu durum sonucunda yapılan değisikliklerin
veritabanına yazılıp yazılmadığını ister programımızdan tekrar 1000 nolu satırı getirerek
bakabiliriz istersekde Sql Server’dan direkt olarak bakabiliriz. Đste sonuçlar.
Sekil 4. Güncelleme isleminin sonucu.
Programımız elbette gelismeye çok, ama çok açık. Örneğin kodumuzda hata denetimi
yapmadığımız bir çok ölü nokta var. Bunların gelistirilmesini siz değerli okurlarımıza
bırakıyorum. Bu makalemizde özetle, bir arayüzü bir sınıfa nasıl uyguladığımızı, bu
arayüzü nasıl yazdığımızı hatırlamaya çalıstık. Ayrıca, sınıfımıza ait bir nesne örneğine, bir
tablodaki belli bir veri satırına ait verileri nasıl alabileceğimizi, bu nesne özelliklerinde
yaptığımız değisiklikleri tekrar nasıl veri tablosuna gönderebileceğimizi inceledik. Böylece
geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüsmek dileğiyle
hepinize mutlu günler dilerim.
Arayüz(Interface), Sınıf(Class) ve Çoklu Kalıtım
Bugünkü makalemizde, arayüzleri incelemeye devam ediceğiz. Arayüzlerin anlatıldığı ilk
makalemizde, arayüzleri kullanmanın en büyük nedenlerinden birisinin sınıflara çoklu
kalıtım desteği vermesi olduğunu söylemistik. Đste bu makalemizde, arayüzlerin,
sınıflara çoklu kalıtım desteğini nasıl sağladığını çok basit bir sekilde incelemeye
çalısacağız. Hiç vakit kaybetmeden, basit bir uygulama üzerinde bunu gösterelim. Bu
uygulamamızda, sınıfımıza, tanımlamıs olduğumuz iki arayüzü uygulayacağız. Böylece
sınıfımız bu iki arayüzü kalıtımsal olarak almıs, çoklu kalıtımı uygulamıs olucak.
using System;
namespace Interfaces2
{
public interface IMusteri /* Đlk arayüzümüzü tanımlıyoruz. */
{
void MusteriDetay();
int ID{get;}
string Isim{get;set;}
string Soyisim{get;set;}
string Meslek{get;set;}
}
public interface ISiparis /* Đkinci arayüzümüzü tanımlıyoruz. */
{
int SiparisID{get;}
string Urun{get;set;}
double BirimFiyat{get;set;}
int Miktar{get;set;}
void SiparisDetay();
}
public class Sepet:IMusteri,ISiparis /* Sepet isimli sınıfımız hem IMusteri
arayüzünü hemde ISiparis arayüzünü uygulayacaktır. */
{
private int id,sipId,mkt;
private string ad,soy,mes,ur;
private double bf;
public int ID
{
get{return id;}
}
public string Isim
{
get{return ad;}
set{ad=value;}
}
public string Soyisim
{
get{return soy;}
set{soy=value;}
}
public string Meslek
{
get{return mes;}
set{mes=value;}
}
public void MusteriDetay()
{
Console.WriteLine(ad+" "+soy+" "+mes);
}
public int SiparisID
{
get{return sipId;}
}
public string Urun
{
get{return ur;}
set{ur=value;}
}
public double BirimFiyat
{
get{return bf;}
set{bf=value;}
}
public int Miktar
{
get{return mkt;}
set{mkt=value;}
}
public void SiparisDetay()
{
Console.WriteLine("----Siparisler----");
Console.WriteLine("Urun:"+ur+" Birim Fiyat"+bf.ToString()+"
Miktar:"+mkt.ToString());
}
}
class Class1
{
static void Main(string[] args)
{
Sepet spt1=new Sepet();
spt1.Isim="Burak";
spt1.Soyisim="Software";
spt1.Meslek="Mat.Müh";
spt1.Urun="Modem 56K";
spt1.BirimFiyat=50000000;
spt1.Miktar=2;
spt1.MusteriDetay();
spt1.SiparisDetay();
}
}
}
Sekil 1. Programın Çalısmasının Sonucu.
Yukarıdaki kodlarda aslında değisik olarak yaptığımız bir sey yok. Sadece
olusturduğumuz arayüzleri bir sınıfa uyguladık ve çoklu kalıtımlılığı gerçeklestirmis
olduk. Ancak bu noktada dikkat etmemiz gereken bir unsur vardır. Eğer arayüzler aynı
isimli metodlara sahip olurlarsa ne olur? Bu durumda arayüzlerin uygulandığı sınıfta,
ilgili metodu bir kez yazmamız yeterli olucaktır. Söz gelimi, yukarıdaki örneğimizde,
Baslat isimli ortak bir metodun arayüzlerin ikisi içinde tanımlanmıs olduğunu
varsayalım.
public interface IMusteri
{
void MusteriDetay();
int ID{get;}
string Isim{get;set;}
string Soyisim{get;set;}
string Meslek{get;set;}
void Baslat();
}
public interface ISiparis
{
int SiparisID{get;}
string Urun{get;set;}
double BirimFiyat{get;set;}
int Miktar{get;set;}
void SiparisDetay();
void Baslat();
}
Simdi bu iki arayüzde aynı metod tanımına sahip. Sınıfımızda bu metodları iki kez
yazmak anlamsız olucaktır. O nedenle sınıfımıza asağıdaki gibi tek bir Baslat metodu
ekleriz. Sınıf nesnemizi olusturduğumuzda, Baslat isimli metodu asağıdaki gibi
çalıstırabiliriz.
spt1.Baslat();
Fakat bazı durumlarda, arayüzlerdeki metodlar aynı isimlide olsalar, arayüzlerin
uygulandığı sınıf içerisinde söz konusu metod, arayüzlerin her biri için ayrı ayrıda
yazılmak istenebilir. Böyle bir durumda ise sınıf içerisindeki metod yazımlarında arayüz
isimlerini de belirtiriz.Örneğin;
void IMusteri.Baslat()
{
Console.WriteLine("Müsteriler hazırlandı...");
}
void ISiparis.Baslat()
{
Console.WriteLine("Siparisler hazırlandı...");
}
Metodların isimleri basında hangi arayüz için yazıldıklarına dikkat edelim. Diğer önemli
bir nokta public belirtecinin kullanılmayısıdır. Public belirtecini kullanmamız durumunda,
"The modifier ’public’ is not valid for this item" (public belirleyicisi bu öğe için geçerli
değildir ) derleyici hatasını alırız. Çünkü, metodumuzu public olarak tanımlamaya gerek
yoktur. Nitekim, bu metodların kullanıldığı sınıflara ait nesnelerden, bu metodları
çağırmak istediğimizde doğrudan çağıramadığımızı görürüz. Çünkü derleyici hangi
arayüzde tanımlanmıs metodun çağırılması gerektiğini bilemez. Bu metodları
kullanabilmek için, nesne örneğini ilgili arayüz tiplerine dönüstürmemiz gerekmektedir.
Bu dönüstürmenin yapılması ilgili sınıf nesnesinin, arayüz tipinden değiskenlere açık bir
sekilde dönüstürülmesi ile olusur. Đste bu yüzdende bu tip metodlar, tanımlandıkları
sınıf içinde public yapılamazlar. Bu açıkça dönüstürme islemide asağıdaki örnek
satırlarda görüldüğü gibi olur.
IMusteri m=(IMusteri)spt1;
ISiparis s=(ISiparis)spt1;
Đste simdi istediğimiz metodu, bu değisken isimleri ile birlikte asağıdaki örnek satırlarda
olduğu gibi çağırabiliriz.
m.Baslat();
s.Baslat();
Sekil 2. Programın Çalısmasının Sonucu.
Gördüğünüz gibi arayüzleri yazmak ve sınıflara çoklu kalıtımı amacıyla uygulamak son
derece kolay. Böylece geldik bir makalemizin daha sonuna. Đlerleyen makalelerimizde
arayüzleri incelemeye devam edeceğiz. Hepinize mutlu günler dilerim.
Arayüzler'de is ve as Anahtar Sözcüklerinin Kullanımı
Bugünkü makalemizde, arayüzlerde is ve as anahtar kelimelerinin kullanımını
inceleyeceğiz. Bir sınıfa arayüz(ler) uyguladığımızda, bu arayüzlerde tanımlanmıs
metodları çağırmak için çoğunlukla tercih edilen bir teknik vardır. O da, bu sınıfa ait
nesne örneğini, çalıstırılacak metodun tanımlandığı arayüz tipine dönüstürmek ve bu
sekilde çağırmaktadır. Bu teknik, her seyden önce, program kodlarının okunabilirliğini ve
anlasılabilirliğini arttırmaktadır. Öyleki, bir isim uzayında yer alan çok sayıda arayüzün ve
sınıfın yer aldığı uygulamalarda be tekniği uygulayarak, hangi arayüze ait metodun
çalıstırıldığı daha kolay bir sekilde gözlemlenebilmektedir. Diğer yandan bu teknik, aynı
metod tanımlamalarına sahip arayüzler için de kullanılır ki bunu arayüzlere çoklu kalıtımın
nasıl uygulandığını gösteren bir önceki makalemizde islemistik.
Bu teknik ile ilgili olarak, dikkat edilmesi gereken bir noktada vardır. Bir sınıfa ait nesne
örneğini, bu sınıfa uygulamadığımız bir arayüze ait herhangibir metodu çalıstırmak için,
ilgili arayüz tipine dönüstürdüğümüzde InvalidCastException istisnasını alırız. Bu
noktayı daha iyi vurgulayabilmek için asağıdaki örneğimizi göz önüne alalım. Bu örnekte
iki arayüz yer almakta olup, tanımladığımız sınıf, bu arayüzlerden sadece bir tanesini
uygulamıstır. Ana sınıfımızda, bu sınıfa ait nesne örneği, uygulanmamıs arayüz tipine
dönüstürülmüs ve bu arayüzdeki bir metod çağırılmak istenmistir.
using System;
namespace Interface3
{
public interface IKullanilmayan
{
void Yaz();
void Bul();
}
public interface IKullanilan
{
void ArayuzAdi();
}
public class ASinifi:IKullanilan
{
public void ArayuzAdi()
{
Console.WriteLine("Arayüz adl:IKullanilan");
}
}
class Class1
{
static void Main(string[] args)
{
ASinifi a=new ASinifi();
IKullanilan Kul=(IKullanilan)a;
Kul.ArayuzAdi();
IKullanilmayan anKul=(IKullanilmayan)a;
anKul.Yaz();
}
}
}
Bu örneği derlediğinizde herhangibi derleyici hatası ile karsılasmassınız. Ancak çalısma
zamanında "System.InvalidCastException: Specified Cast Is Invalid" çalısma
zamanı hatasını alırız. Đste bu sorunu is veya as anahtar sözcüklerinin kullanıldığı iki
farklı teknikten birisi ile çözebiliriz. Is ve as bu sorunun çözümünde aynı amaca hizmet
etmekle beraber aralarında önemli iki fark vardır. Is anahtar kelimesi asağıdaki
formasyonda kullanılır.
nesne is tip
Is anahtar kelimesi nesne ile tipi karsılastırır. Yani belirtilen nesne ile, bir sınıfı veya
arayüzü kıyaslarlar. Bu söz dizimi bir if karsılastırmasında kullanılır ve eğer nesnenin
üretildiği sınıf, belirtilen tip’teki arayüzden uygulanmıssa bu kosullu ifade true değerini
döndürecektir. Aksi durumda false değerini döndürür. Simdi bu tekniği yukarıdaki
örneğimize uygulayalım. Yapmamız gereken değisiklik Main metodunda yer almaktadır.
static void Main(string[] args)
{
ASinifi a=new ASinifi();
IKullanilan Kul=(IKullanilan)a;
Kul.ArayuzAdi();
if(a is IKullanilmayan)
{
IKullanilmayan anKul=(IKullanilmayan)a;
anKul.Yaz();
}
else
{
Console.WriteLine("ASinifi, IKullanilmayan arayüzünü uygulamamıstır.");
}
}
Sekil 1: is Anahtar Kelimesinin Kullanımı.
If kosullu ifadesinde, a isimli nesneyi olusturduğumuz ASinifi sınıfına, IKullanilmayan
arayüzünü uygulayıp uyguladığımızı kontrol etmekteyiz. Sonuç false değerini
döndürecektir. Nitekim, ASinifi sınıfına, IKullanilmayan arayüzünü uygulamadık.
Is anahtar sözcüğü arayüzler dısında sınıflar içinde kullanabiliriz. Bununla birlikte is
anahtar sözcüğü kullanıldığında, program kodu Intermediate Language (IL) (Ortak Dile)
çevrildiğinde, yapılan denetlemenin iki kere tekrar edildiğini görürüz. Bu verimliliği
düsürücü bir etkendir. Đste is yerine as anahtar sözcüğünü tercih etmemizin
nedenlerinden biriside budur. Diğer taraftan is ve as teknikleri, döndürdükleri değerler
bakımından da farklılık gösterir. Is anahtar kelimesi, bool tipinde ture veya false
değerlerini döndürür. As anahtar kelimesi ise, bir nesneyi , bu nesne sınıfına uygulanmıs
bir arayüz tipine dönüstürür. Eğer nesne sınıfı, belirtilen arayüzü uygulamamıssa,
dönüstürme islemi yinede yapılır, fakat dönüstürülmenin aktarıldığı değisken null
değerine sahip olur. As anahtar kelmesinin formu asağıdaki gibidir.
sınıf nesne1=nesne2 as tip
Burada eğer nesneye belirtilen tipi temsil eden arayüz uygulanmamıssa, nesne null
değerini alır. Aksi durumda nesne belirtilen tipe dönüstürülür. Đste is ile as arasındaki
ikinci farkta budur. Konuyu daha iyi anlayabilmek için as anahtar kelimesini yukarıdaki
örneğimize uygulayalım.
static void Main(string[] args)
{
ASinifi a=new ASinifi();
IKullanilan Kul=(IKullanilan)a;
Kul.ArayuzAdi();
IKullanilmayan anKul=a as IKullanilmayan;
if(anKul!=null)
{
anKul.Yaz();
}
else
{
Console.WriteLine("ASinifi IKullanilmayan tipine dönüstürülemedi");
}
}
Burada a nesnemiz ASinifi sınıfının örneğidir. As ile bu örneği IKullanilmayan arayüzü
tipinden anKul değiskenine aktarmaya çalısıyoruz. Đste bu noktada, ASinifi,
IKullanilmayan arayüzünü uygulamadığı için, anKul değiskeni null değerini alıcaktır.
Sonra if kosullu ifadesi ile, anKul ’un null olup olmadığı kontrol ediyoruz. Uygulamayı
çalıstırdığımızda asağıdaki sonucu elde ederiz.
Sekil 2: as Anahtar Kelimesinin Kullanımı
Geldik bir makalemizin daha sonuna. Bir sonraki makalemizde görüsmek dileğiyle
hepinize mutlu günler dilerim.
Sınıf (Class) Kavamına Giris
Bu makalemizde ADONET kavramı içerisinde sınıfları nasıl kullanabileceğimizi incelemeye
çalısacak ve sınıf kavramına kısa bir giris yapıcağız. Nitekim C# dili tam anlamıyla nesne
yönelimli bir dildir. Bu dil içerisinde sınıf kavramının önemli bir yeri vardır. Bu kavramı iyi
anlamak, her türlü teknikte, sınıfların avantajlarından yararlanmanızı ve kendinize özgü
nesnelere sahip olabilmenizi sağlar. Zaten .net teknolojisinde yer alan her nesne,
mutlaka sınıflardan türetilmektedir.
Çevremize baktığımız zaman, çok çesitli canlılar görürüz. Örneğin çiçekler. Dünya
üzerinde kaç tür(cins) çiçek olduğunu bileniniz var mı ? Ama biz bir çiçek gördüğümüzde
ona çoğunlukla “Çiçek” diye hitap ederiz özellikle adını bilmiyorsak. Sonra ise bu çiçeğin
renginden, yapraklarının seklinden, ait olduğu türden, adından bahsederiz. Çiçek tüm bu
çiçekler için temel bir sınıf olarak kabul edilebilir. Dünya üzerindeki tüm çiçekler için ortak
nitelikleri vardır. Her çiçeğin bir renginin(renklerinin) olması gibi. Đste nesne yönelimli
programlama kavramında bahsedilen ve her seyin temelini olusturan sınıf kavramı bu
benzetme ile tamamen aynıdır. Çiçek bir sınıf olarak algılanırken, sokakta gördüğümüz
her çiçek bu sınıfın ortak özelliklerine sahip birer nesne olarak nitelendirilebilir. Ancak
tabiki çiçekler arasında da türler mevcuttur. Bu türler ise, Çiçek temel sınıfından türeyen
kendi belirli özellikleri dısında Çiçek sınıfının özelliklerinide kalıtsal olarak alan baska
sınıflardır. Bu yaklasım inheritance (kalıtım) kavramı olarak ele alınır ve nesne yönelimli
programlamanın temel üç öğesinden biridir. Kalıtım konusuna ve diğerlerine ilerliyen
makalelerimizde değinmeye çalısacağız.
Bugün yapacağımız bir sınıfın temel yapı taslarına kısaca değinmek ve kendimize ait
isimize yarayabiliecek bir sınıf tasarlamak. Çiçek sınıfından gerçek C# ortamına
geçtiğimizde, her seyin bir nesne olduğunu görürüz. Ancak her nesne temel olarak Object
sınıfından türemektedir. Yani herseyin üstünde bir sınıf kavramı vardır. Sınıflar, bir takım
üyelere sahiptir. Bu üyeler, bu sınıftan örneklendirilen nesneler için farklı değerlere sahip
olurlar. Yani bir sınıf varken, bu sınıftan örneklendirilmis n sayıda nesne olusturabiliriz.
Kaldıki bu nesnelerin her biri, tanımlandığı sınıf için ayrı ayrı özelliklere sahip olabilirler.
Sekil 1. Sınıf (Class) ve Nesne (Object) Kavramı
Bir sınıf kendisinden olusturulacak nesneler için bir takım üyeler içermelidir. Bu üyeler,
alanlar (fields), metodlar (methods), yapıcılar (constructor), özellikler (properties),
olaylar(events), delegeler (delegates) vb… dır. Alanlar verileri sınıf içerisinde tutmak
amacıyla kullanılırlar. Bir takım islevleri veya fonksiyonellikleri gerçeklestirmek için
metodları kullanırız. Çoğunlukla sınıf içinde yer alan alanların veya özelliklerin ilk
değerlerin atanması gibi hazırlık islemlerinde ise yapıcıları kullanırız. Özellikler kapsülleme
dediğimiz Encapsulating kavramının bir parçasıdır. Çoğunlukla, sınıf içersinden
tanımladığımız alanlara, dısarıdan doğrudan erisilmesini istemeyiz. Bunun yerine bu
alanlara erisen özellikleri kullanırız. Đste bu sınıf içindeki verileri dıs dünyadan
soyutlamaktır yani kapsüllemektir. Bir sınıfın genel hatları ile içereceği üyeleri asağıdaki
sekilde de görebilirsiniz.
Sekil 2 . Bir sınıfın üyeleri.
Sınıflar ile ilgili bu kısa bilgilerden sonra dilerseniz sınıf kavramını daha iyi anlamamızı
sağlıyacak basit bir örnek gelistirelim. Sınıflar ve üyeleri ile ilgili diğer kavramları kodlar
içerisinde yer alan yorum satırlarında açıklamaya devam edeceğiz. Bu örnek
çalısmamızda, Sql Suncusuna bağlanırken, bağlantı islemlerini kolaylastıracak birtakım
üyeler sağlıyan bir sınıf gelistirmeye çalısacağız. Kodları yazdıkça bunu çok daha iyi
anlayacaksınız. Đste bu uygulama için gelistirdiğimiz, veri isimli sınıfımızın kodları.
using System;
using System.Data.SqlClient;
namespace Veriler /* Sınıfımız Veriler isimli isim uzayında yer alıyor. Çoğu zaman aynı
isme sahip sınıflara sahip olabiliriz. Đste bu gibi durumlarda isim uzayları bu sınıfların
birbirinden farklı olduğunu anlamamıza yardımcı olurlar.*/
{
public class Veri /* Sınıfımızın adı Veri */
{
/* Đzleyen satırlarda alan tanımlamalarının yapıldığını görmekteyiz. Bu alanlar
private olarak tanımlanmıstır. Yani sadece bu sınıf içerisinden erisilebilir ve değerleri
değistirilebilir. Bu alanları tanımladığımız özelliklerin değerlerini tutmak amacıyla
tanımlıyoruz. Amacımız bu değerlere sınıf dısından doğrudan erisilmesini engellemek.*/
private string SunucuAdi;
private string VeritabaniAdi;
private string Kullanici;
private string Parola;
private SqlConnection Kon; /* Burada SqlConnection tipinden bir değisken
tanımladık. */
private bool BaglantiDurumu; /* Sql sunucumuza olan bağlantının açık olup
olmadığına bakıcağız.*/
private string HataDurumu; /* Sql sunucusuna bağlanırken hata olup olmadığına
bakacağız.*/
/* Asağıda sunucu adında bir özellik tanımladık. Herbir özellik, get veya set
bloklarından en az birini içermek zorundadır. */
public string sunucu /* public tipteki üyelere sınıf içinden, sınıf dısından veya
türetilmis sınıflardan yani kısaca heryerden erisilebilmektedir.*/
{
get
{
return SunucuAdi; /* Get ile, sunucu isimli özelliğe bu sınıfın bir
örneğinden erisildiğinde okunacak değerin alınabilmesi sağlanır . Bu değer bizim private
olarak tanımladığımız SunucuAdi değiskeninin değeridir. */
}
set
{
SunucuAdi=value; /* Set bloğunda ise, bu özelliğe, bu sınıfın bir
örneğinden değer atamak istediğimizde yani özelliğin gösterdiği private SunucuAdi
alanının değerini değistirmek için kullanırız. Özelliğe sınıf örneğinden atanan değer,
value olarak tasınmakta ve SunucuAdi alanına aktarılmaktadır.*/
}
}
public string veritabani
{
get
{
return VeritabaniAdi;
}
set
{
VeritabaniAdi=value;
}
}
public string kullanici /* Bu özellik sadece set bloğuna sahip olduğu için sadece
değer atanabilir ama içeriği görüntülenemez. Yani kullanici özelliğini bir sınıf örneğinde,
Kullanici private alanının değerini öğrenmek için kullanamayız.*/
{
set
{
Kullanici=value;
}
}
public string parola
{
set
{
Parola=value;
}
}
public SqlConnection con /* Buradaki özellik SqlConnection nesne türündendir ve
sadece okunabilir bir özelliktir. Nitekim sadece get bloğuna sahiptir. */
{
get
{
return Kon;
}
}
public bool baglantiDurumu
{
get
{
return BaglantiDurumu;
}
set /* Burada set bloğunda baska kodlar da ekledik. Kullanıcımız bu sınıf
örneği ile bir Sql bağlantısı yarattıktan sonra eğer bu bağlantıyı açmak isterse
baglantiDurumu özelliğine true değerini göndermesi yeterli olucaktır. Eğer false değeri
gönderirse bağlantı kapatılır. Bu islemleri gerçeklestirmek için ise BaglantiAc ve
BaglantiKapat isimli sadece bu sınıfa özel olan private metodlarımızı kullanıyoruz.*/
{
BaglantiDurumu=value;
if(value==true)
{
BaglantiAc();
}
else
{
BaglantiKapat();
}
}
}
public string hataDurumu
{
get
{
return HataDurumu;
}
}
public Veri() /* Her sınıf mutlaka hiç bir parametresi olmayan ve yandaki satırda
görüldüğü gibi, sınıf adı ile aynı isme sahip bir metod içerir. Bu metod sınıfın yapıcı
metodudur. Yani Constructor metodudur. Bir yapıcı metod içersinde çoğunlukla, sınıf
içinde kullanılan alanlara baslangıç değerleri atanır veya ilk atamalar yapılır. Eğer siz bir
yapıcı metod tanımlamaz iseniz, derleyici aynen bu metod gibi bos bir yapıcı olusturacak
ve sayısal alanlara 0, mantıksal alanlara false ve string alanlara null baslangıç
değerlerini atayacaktır.*/
{
}
/* Burada biz bu sınıfın yapıcı metodunu asırı yüklüyoruz. Bu sınıftan bir nesneyi
izleyen yapılandırıcı ile olusturabiliriz. Bu durumda yapıcı metod içerdiği dört
parametreyi alıcaktır. Metodun amacı ise belirtilen değerlere göre bir Sql bağlantısı
yaratmaktır.*/
public Veri(string sunucuAdi,string veritabaniAdi,string kullaniciAdi,string sifre)
{
SunucuAdi=sunucuAdi;
VeritabaniAdi=veritabaniAdi;
Kullanici=kullaniciAdi;
Parola=sifre;
Baglan();
}
/* Burada bir metod tanımladık. Bu metod ile bir Sql bağlantısı olusturuyoruz.
Eğer bir metod geriye herhangibir değer göndermiyecek ise yani vb.net teki
fonksiyonlar gibi çalısmayacak ise void olarak tanımlanır. Ayrıca metodumuzun sadece
bu sınıf içerisinde kullanılmasını istediğimiz için private olarak tanımladık. Bu sayede bu
sınıf dısından örneğin formumuzdan ulasamamalarını sağlamıs oluyoruz.*/
private void Baglan()
{
SqlConnection con=new SqlConnection("data source="+SunucuAdi+";initial
catalog="+VeritabaniAdi+";user id="+Kullanici+";password="+Parola);
Kon=con;
}
/* Bu metod ile Sql sunucumuza olan bağlantıyı açıyoruz ve BaglantiDurumu
alanına true değerini aktarıyoruz.*/
private void BaglantiAc() /* Bu metod private tanımlanmıstır. Çünkü sadece bu
sınıf içerisinden çağırılabilsin istiyoruz. */
{
Kon.Open();
try
{
BaglantiDurumu=true;
HataDurumu="Baglanti sağlandi";
}
catch(Exception h)
{
HataDurumu="Baglanti Sağlanamdı. "+h.Message.ToString();
}
}
/* Bu metod ilede Sql bağlantımızı kapatıyor ve BaglantiDurumu isimli alanımıza
false değerini akatarıyoruz.*/
private void BaglantiKapat()
{
Kon.Close();
BaglantiDurumu=false;
}
}
}
Simdi ise sınıfımızı kullandığımız örnek uygulama formunu tasarlayalım . Bu uygulamamız
asağıdaki form ve kontrollerinden olusuyor.
Sekil 3. Form Tasarımımız.
Formumuza ait kodlar ise söyle.
Veriler.Veri v;
private void btnBaglan_Click(object sender, System.EventArgs e)
{
/* Bir sınıf örneği yaratmak için new anahtar kelimesini kullanırız. New anahtar
kelimesi bize kullanabileceğimiz tüm yapıcı metodları gösterecektir. (IntelliSense
özelliği). */
v=new Veri(txtSunucu.Text,txtVeritabani.Text,txtKullanici.Text,txtParola.Text);
}
private void btnAc_Click(object sender, System.EventArgs e)
{
v.baglantiDurumu=true;
stbDurum.Text="Sunucu Bağlantısı Açık? "+v.baglantiDurumu.ToString();
}
private void btnKapat_Click(object sender, System.EventArgs e)
{
v.baglantiDurumu=false;
stbDurum.Text="Sunucu Bağlantısı Açık? "+v.baglantiDurumu.ToString();
}
Simdi uygulamamızı bir çalıstıralım.
Sekil 5. Bağlantıyı açmamız halinde.
Sekil 6. Bağlantıyı kapatmamız halinde.
Değerli okurlarım, ben bu sınıfın gelistirilmesini size bırakıyorum. Umarım sınıf kavramı
ile ilgili bilgilerimizi hatırlamıs ve yeni ufuklara yelken açmaya hazır hale gelmissinizdir.
Bir sonraki makalemizde sınıflar arasında kalıtım kavramına bakıcak ve böylece nesneye
dayalı programlama terminolojisinin en önemli kavramlarından birini incelemeye
çalıssacağız. Hepinize mutlu günler dilerim.
Burak Selim SENYURT