Memik Yanık - Kişisel Web Sitesi Ana Sayfa | Hakkımda | Kitap Önerileri | Fotoğraflarım | RSS RSS | İletişim

C# Projelerini Taklit Etmek


Visual Basic kitabımdan yararlanarak hazırladığım bu makalenin içeriğinin benzerine yerli ve yabancı kaynaklarda rastlamanız düşük bir ihtimaldir. Hemen soracaksanız; Dünyada Visual Basic veya programcılıkla ilgili olarak söylenmedik bir şey var mıdır? Programcılık teknikleri bağlamında bu soru ele alındığında verilecek cevap şudur:Hayır yoktur. Buradaki amacımız programcılıkla ilgili yeni bir teknik bulmak ve programcıların istifadesine bu tekniği sunmak değildir. Bu makaledeki amacımız; .NET uyumlu Visual Basic uygulamalarının anlaşılmasına katkı vermektir. Başka bir deyişle, anlatma tekniğidir bu makalede yeni olan, başka kaynaklarda bulunma ihtimalinin düşük olduğu bir anlatım ve konuyu ele alış yaklaşımıdır. 

Öncelikle meraklısı için C# projeleri için buraya birkaç cümle yazacağız. Visual Studio ile hazırlanan masaüstü C# uygulamalarında “Program.cs” adında bir dosya bulunmaktadır ve C# uygulamaları çalışmaya Program.cs dosyasından başlamaktadır. Çünkü Main() metodu bu dosyada bulunmaktadır. Delphi’yi geliştiren ekip Borland’dan ayrılıp Microsoft’a geçip C#’ı geliştiren ekibe katılınca C# projeleri için Delphi’nin proje dosyası ile benzer işleve sahip “Program.cs” dosyası hazırlandı. Visual Studio 2003 ile hazırlanan C# projelerinde Program.cs dosyası yoktur. 

Her ne kadar aynı sınıfları kullansalar bile Visual Basic projelerinin organizasyonu C#’tan oldukça farklıdır ve Visual Studio ile hazırlanan Visual Basic projeleri daha karmaşıktır. Konunun kolay kavranmasını sağlamak için, başka bir deyişle .NET uyumlu Visual Basic projelerinin nasıl organize edildiğini anlatmadan önce C# projelerinin yapısını taklit edip bir konsol uygulaması hazırlayacağız. Visual Studio ile hazırlanan Visual Basic projeleri hakkında bilgiyi aşağıda linkini verdiğimiz makalede bulalirsiniz.

http://www.memikyanik.net/application-sinifi-ve-visual-basic-projeleri-makalesi/30.aspx 

Visual Studio ile konsol uygulaması hazırladığınızda ilgilendiğiniz namespace’leri içeren referans veya DLL dosyalarını Solution Explorer penceresinde kolayca listeleyebiliyorsunuz. Ancak Visual Studio’dan yararlanma­dan hazırladığınız konsol uygulamasında System ve Microsoft.VisualBasic dışında herhangi bir namespace’in kaynaklarından yararlandığınızda derleme yaparken bu namespace’in yer aldığı referans veya DLL dosyasından derleyiciyi haberdar etmeniz gerekiyor. Bu konuda bilgi vermek için aşağı­daki gibi basit bir konsol uygulaması hazırladık.


Sizin için herhangi bir yenilik içermeyen bu kodu dikkatlice incelerseniz bu kodda System adlı namespace’te dolayısıyla “System.dll” dosyasında yer alan Console sınıfından yararlanıldı­ğını görürsünüz. DOS penceresinde derleme işlemini aşağıdaki gibi yapmamıza rağmen, başka bir deyişle derleyiciyi System.dll’den haberdar etmemiş olmamıza rağmen derleme başarılı bir şekilde gerçekleşti.



Çünkü Visual Basic derleyicisi System.dll ve Microsoft.VisualBasic.dll dosyalarını otomatik olarak refere etmektedir. Bu nedenle konsolda çalıştırılmak üzere hazırlanan bu Visual Basic programı aşağıdaki gibi derlense bile değişen bir şey olmaz.



Şimdi yukarıda verilen basit konsol uygulamasında değişiklik yapıp System.Windows.Forms’deki Form ve Application sınıflarından yararlanacağız.

Option Strict On
Option Explicit On
Imports System.Windows.Forms
Namespace Deneme 
  Public Class Form1 
         Inherits System.Windows.Forms.Form 
     Sub New() 
       Me.Text = "Form1" 
     End Sub 
  End Class 
  Public Class anasinif 
    Shared Sub Main() 
       Dim nesne as new Form1() 
       Application.Run(nesne) 
    End Sub 
  End Class
End Namespace 

2 sınıfın olduğu bu konsol uygulamasındaki Form1 adlı sınıf System.Windows.Forms’deki Form sınıfının mirasçısı olup yapıcı metodunda Text özelliği ayarlanmaktadır. Main() metodunun olduğu Class’ta(anasinif adlı sınıf) ise ilk sınıftan yola çıkılıp nesne hazırlanıp bu form nesnesi Application sınıfının Run() metoduna parametre olarak verilmektedir. Bu sayede uygulamanın Windows’la olan bağlantısı kurulmaktadır. 

Bu kodu derleyip çalıştırdığınızda “Form1” başlıklı bir pencerenin ekrana geleceğini tahmin edebilirsiniz. Bu uygulamada “System.Windows.Forms.dll” adlı reference’ten yararlanıldığı için derleme yapılırken bu DLL dosyasının aşağıdaki gibi reference parametresi ile işaret edilmesi gerekir. Bu derleme satırında reference parametresini 2. kez kullanıp System.dll’den veya başka bir referanstan derleyici haberdar edilebilinir. 



N
ot Defteri veya başka bir şekilde hazırladığınız uygulamayı DOS penceresinde kendiniz derlerken uygulamanın konsol uygulaması olduğu varsayılır. Bu nedenle yukarıdaki şekilde derlenen projenin EXE dosyası çalıştırıldığında önce bir DOS penceresi açılır sonra proje dahilinde hazırlanan form ekrana gelir. Aşağıda verilen ekran görüntüsünü yukarıda anlattı­ğımız şekilde hazırladığımız EXE dosyayı çalıştırdıktan sonra aldık.



Her ne kadar bu uygulamanın konsolla bir işi olmasa bile bu uygulama yukarıdaki sayfalarda anlatılan şekilde derlenirse Target parametresinin “exe” olduğuna karar verilir ve Windows formu ile birlikte DOS penceresi de açılır. DOS penceresinde derlediğiniz uygulama Windows Forms uygulaması ise derlemeyi aşağıdaki gibi yapabilirsiniz. Target parametresi winexe’den başka library ve module olabilmektedir.



Bu şekilde Visual Studio’dan yararlanmadan Not Defteri ile Windows Forms uygulamasının nasıl hazırlandığını gördükten sonra şimdi Visual Studio’yu kullanmadan Visual Studio ile hazırlanan C# uygulamalarının benzeri bir Visual Basic uygulaması hazırlayacağız. Bu amaçla “C:” sürücüsünün kök klasöründe “\Projem” adında yeni bir klasör hazırlayıp Not Defteri ile aşağıdaki gibi bir “Program.vb” dosyası hazırlayıp kaydettik. Bu dosyaya “Program.vb” yerine başka bir ad verebilirdik. Madem C# projelerini taklit ediyoruz en ideali bu dosyaya Program.vb adını vermektir. Bu dosyanın önemi Main() metodunu içermesidir.



.NET uyumlu Visual Basic uygulamalarının başlangıç noktasının Main() metodu olması gibi bir şart olmasa bile tıpkı C# uygulamalarında olduğu gibi Main() adında bir metot hazırladık. Bu şekilde Visual Studio’nun hazırladığından biraz kısıtlı bir “Program.vb” dosyasını ilgili klasöre kaydettikten sonra yine Not Defteri aşağıdaki gibi bir “Form1.vb” dosyası hazırladık. Bu kod dosyasında Form1 adlı sınıfın yapıcı metodunda initializeComponent() metodu işletilmektedir.



Visual Studio tarafından Imports deyimi ile koda dahil edilen bazı namepace’lere bu kodda gerek duymayacağımız için işaret etmedik. Aslında Imports deyimi ile işaret edilen namespace’lerin koda dahil edildiğini söylemek doğru değildir. Çünkü koda dahil edilen bir şey yoktur ve söz konusu DLL dosyası yerinde durmaktadır. Şimdi sırada “Form1” adlı Partial Class’ın diğer kısmını yazmaya geldi. Bu amaçla Not Defteri ile aşağıdaki gibi bir “Form1.Designer.vb” dosyası hazırladık.



Bu şartlarda hazırladığımız Visual Basic uygulamasının kodları 3 ayrı VB uzantılı kod dosyasında bulunmakta ve System.Windows.Forms’dan yararlanılmaktadır. Bu namespace System.Windows.Forms.dll referansında yer aldığı için derleme satırını aşağıdaki gibi düzenledik. Tahmin edeceğiniz gibi üzerinde çalışılan konsol uygulamasında birden fazla referanstan yararlanıldığı zaman derleme satırı daha uzun olacaktır. System.dll dosyası derleme sırasında uygulamaya otoma­tik olarak dahil edildiği için derleme satırına yazmaya gerek duymadık. 

C:\Projem>VBC /Target:Winexe Program.vb Form1.vb Form1.Designer.vb /reference: System.Windows.Forms.dll /out:Projem.exe 

Bu şekilde yapılan derleme sonucu “Projem.exe” adında bir EXE dosya hazırlanır. Her ne kadar bu program dosyası çalıştırıldığında “Form1” başlıklı bir pencere ekrana gelse bile bu uygulama konsol uygulaması sayılmaktadır. Uygulamanın Windows Form uygulaması olarak değerlendirilmesini sağlamak için /target:winexe parametresi ile derlemek gerekmektedir. 

Şimdi Not Defteri ile hazırladığımız bu proje dahilinde ekrana getirilen forma bir düğme veya Button nesnesi yerleştireceğiz. Bu amaçla “Form1” sınıfının “Form1.Designer.vb” dosyasındaki InitializeComponent() metodunu aşağıdaki gibi düzenledik. 

Option Strict On
Option Explicit On
Imports System.Windows.Forms
Namespace Projem 
  Public Partial Class Form1 
         Inherits System.Windows.Forms.Form 
      Dim Button1 As System.Windows.Forms.Button 
      Sub initializeComponent() 
              Me.Text ="Form1" 
              Me.Button1 = new System.Windows.Forms.Button() 
              Me.Button1.Name ="Button1" 
              Me.Button1.Location = new System.Drawing.Point(75, 50) 
              Me.Button1.Size = New System.Drawing.Size(75, 25) 
              Me.Button1.Text ="Button1" 
              Me.Controls.Add(Me.Button1) 
          End Sub 
   End Class
End Namespace 

Bu şekilde 3 dosya şeklinde düzenlenen konsol uygulaması derlenip çalıştırıldığında aşağıdaki gibi bir sonuç alınır. Formdaki bu düğme tıklandığı zaman işletilmek üzere kod yazmak mümkün olmakla birlikte Visual Studio dururken Not Defteri ile proje geliştirmek pek mantıklı olmayacağı için konuyu fazla uzatmadık.

VB.NET | Bu yazıya henüz yorum yapılmamış. | 03.05.2010 20:30:41

GetEnumerator Metodu


ArrayList sınıfı gibi koleksiyon sınıflarının çoğu IEnumerator arayüzünü uyguladığı için bu sınıfların GetEnumerator() metotları bulunmaktadır. Bu metot sayesinde söz konusu koleksiyon nesnesinin elemanlarını bir numaralayıcıya(enum) alıp bazı işlem­leri kolayca yapabilirsiniz. Bu metot hakkında bilgi vermek için Visual Studio ile hazırladığımız projede aşağıdaki gibi kod yazıp birkaç elemana sahip bir ArrayList nesnesi hazırladık. 

Dim
Liste As ArrayList
Private Sub Form1_Load(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles MyBase.Load 
   Liste = New ArrayList(10
   Liste.Add("Hard Disk"
   Liste.Add("Yazıcı"
   Liste.Add("Ekran"
   Liste.Add("İşlemci"
   Liste.Add("Bellek"
   Liste.Add("Kasa")
EndSub 

ArrayList nesnesini Class’taki bütün metotlarda kullanabilmek için değişken tanımlamayı metot dışında yaptık. Bu şekilde ArrayList nesnesi hazırladıktan sonra ArrayList sınıfının GetEnumerator() metodundan yararlanıp
IEnumeratornesnesi hazırlayacağız. Bu amaçla yukarıda verilen koda ekleme yapıp IEnumerator nesnesi hazırladık. 

Dim
Liste As ArrayList
Dim enum_nesnesi As IEnumerator
Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVale As System.EventArgs) Handles MyBase.Load 
   Liste = New ArrayList(10
   Liste.Add("Hard Disk"
   Liste.Add("Yazıcı"
   Liste.Add("Ekran"
   Liste.Add("İşlemci"
   Liste.Add("Bellek"
   Liste.Add("Kasa"
   enum_nesnesi = Liste.GetEnumerator()
EndSub 

Bu şekilde hazırlanan
IEnumerator tipindeki koleksiyonun elemanları üzerinde ileri doğru hareket edip elemanlarının içeriğini tek tek elde edebiliriz. Bu amaçla Visual Studio ile hazırladığımız Visual Basic uygulamasının formuna bir TextBox yerleştirip yukarıda verilen kodu aşağıdaki gibi düzenledik. 

Dim
Liste As ArrayList
Dim enum_nesnesi As IEnumerator
Private Sub Form1_Load(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles MyBase.Load 
   Liste = New ArrayList(10
   Liste.Add("Hard Disk"
   Liste.Add("Yazıcı"
   Liste.Add("Ekran"
   Liste.Add("İşlemci"
   Liste.Add("Bellek"
   Liste.Add("Kasa"
   enum_nesnesi = Liste.GetEnumerator() 
   enum_nesnesi.MoveNext() 
   TextBox1.Text = enum_nesnesi.Current.ToString()
EndSub 

Bu kod sayesinde IEnumerator tipindeki koleksiyonun ilk elemanın içeriği TextBox’a aktarılır. Forma bir düğme yerleştirip MoveNext() metodunu tekrar işleterek koleksiyondaki bir sonraki elemanın üzerine gidip bu elemanın içeriğini elde edebilirsiniz. Bu işlemin nasıl yapıldığını aşağıda görebilirsiniz. 

Private
Sub sonraki_Click(ByVal sender As System.Object, _
        ByVale As System.EventArgs) Handles sonraki.Click 
  enum_nesnesi.MoveNext() 
  TextBox1.Text = enum_nesnesi.Current.ToString()
EndSub 

Koleksiyondaki en son elemanın üzerinde iken bu kod işletilirse 2. satır hataya neden olur. Bu hatanın önüne geçmek için MoveNext() metodunun geriye gönderdiği değerden yararlanılır. Koleksiyondaki bir sonraki elemanın üzerine gitmek üzere MoveNext() metodu işletildi­ğinde geriye False veya True gönderilir. Koleksiyonun en son elemanının üzerinde iken MoveNext metodu işletildiğinde hareket başarılı olamayacağı için MoveNext() geriye False gönderir. Bu nedenle MoveNext() metodunun geriye gönderdiği boolean sonuç aşağıdaki gibi kontrol edilip olası hataların önüne geçilir. 

Private
Sub sonraki_Click(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles sonraki.Click 
  Dim Sonuc As Boolean 
  Sonuc = enum_nesnesi.MoveNext() 
  If Sonuc = True Then 
     TextBox1.Text = enum_nesnesi.Current.ToString() 
  Else      
    MessageBox.Show("Koleksiyonun son elemanın üzerindesiniz"
  End If
EndSub 

Elbette ArrayList nesnesinin elemanları üzerinde hareket edip istenen elemanın içeriğini elde etmek mümündür. Ancak GetEnumerator() metodu ile hazırlanan koleksiyonun üzerinde haraket etmek daha kolaydır. Koleksiyonun yani Enum nesnesinin üzerinde olunan geçerli elema­nın içeriği Current özelliği ile elde edilmektedir. GetEnumerator() metodu ile hazırlanan koleksiyonun ilk elemanın üzerine gidilmek isten­diğinde Reset() metodu aşağıdaki gibi kullanılmalıdır. 

enum_nesnesi.Reset() 

GetEnumerator
() metodu ile hazırlanan enum nesnesinin elemanlarının nasıl kullanıldığına farklı bir örnek olması için Visual Basic uygulamasının formuna 2 ListBox yerleştirdik. İlk ListBox’a tasarım anında bir futbol takımının adını aktardık.



Kullanıcı çalışma anında formdaki düğmeyi tıkladığında ilk ListBox’ın içeriği önce ArrayList nesnesine, oradan GetEnumerator() metodu ile hazırlanan enum tipine aktaracağız. En son olarak enum nesnesinin içeriğini formdaki 2. ListBox’a aktaracağız. Bu işlemi yapmak üzere naklet* adını verdiğimiz düğmenin Click olayını temsil eden metodu aşağıdaki gibi düzenledik. 

Private
Sub naklet_Click(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles naklet.Click 
  Dim Takimlar As New ArrayList() 
  For Each takim As Object In ListBox1.Items
    Takimlar.Add(takim
  Next 
  Dim koleksiyon As IEnumerator 
  koleksiyon = Takimlar.GetEnumerator 
  While (koleksiyon.MoveNext()) 
    ListBox2.Items.Add(koleksiyon.Current.ToString()) 
  End While
EndSub 

Yukarıda işaret edildiği üzere .NET Framework ile gelen koleksiyon sınıflarının hemen hepsi
GetEnumerator() metoduna sahiptir. Bu metodun işlevinin iyi anlaşılması için şimdi bir Queue nesnesi hazırlayıp bu nesnenin mevcut elemanlarını GetEnumerator() metodu ile enum koleksiyonuna aktaracağız. Bu amaçla yukarıda hazırladığımız projedeki formdaki ListBox’lardan birisini silip “naklet” adlı düğmenin Click olayını temsil eden metodu aşağıdaki gibi düzenledik. Bu kod işletildiğinde Queue nesnesinin eleman sayısı değişmeden bütün elemanlar elde edilip ListBox’a aktarılır. 

Private
Sub naklet_Click(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles naklet.Click 
   Dim Kuyruk As Queue 
   Kuyruk = New Queue(10
   Kuyruk.Enqueue("İlkbahar"
   Kuyruk.Enqueue("Yaz"
   Kuyruk.Enqueue("Sonbahar"
   Kuyruk.Enqueue("Kış"
   ListBox1.Items.Clear() 
   Dim enum_liste As System.Collections.IEnumerator 
   enum_liste = Kuyruk.GetEnumerator() 
   While (enum_liste.MoveNext()) 
     ListBox1.Items.Add(enum_liste.Current
   End While
EndSub 

GetEnumerator
() metodu hakkında bilgi verirken yukarıda yararlandığımız ArrayList ve Queue nesnelerinin elemanlarında yalnızca değer bilgileri tutulabilmektedir. Elinizde anahtar-değer şeklinde düzenlenmiş bilgileri içerebilen Hashtable gibi bir nesne varsa bu nesnenin içeriğini GetEnumerator() metodu ile IDictionaryEnumerator tipinde bir numaralayıcıya alabilirsiniz. Bu işlemin nasıl yapıldığını anlatmak için aşağıda verilen kodu yazdık. 

Dim
iller As New Hashtable()
Dim
Liste As IDictionaryEnumerator
Private Sub Form1_Load(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles MyBase.Load 
  iller.Add("01", "Adana"
  iller.Add("06", "Ankara"
  iller.Add("07", "Antalya"
  iller.Add("16", "Bursa"
  iller.Add("33", "Mersin"
  iller.Add("34", "İstanbul"
  iller.Add("35", "İzmir"
  Liste = iller.GetEnumerator()
EndSub 

Bu kodda önce “Liste” adında ve IDictionaryEnumerator tipinde bir değişken tanımla­dık. Bu değişkeni diğer metotlarda kullanabilmek için formun Load olayını temsil eden metodunun dışında tanımladım ve GetEnumerator() metodu ile Hashtable nesnesinin içeriğini bu numaralayıcıya kopyala­dık. 

Bu hazırlıktan sonra bu numaralayıcıdan yararlanarak Hashtable nesnesinin anahtar-değer şeklinde düzenlenmiş içeriğine kolayca erişebilirsiniz. Proje çalıştırıldığında HashTable nesnesinin ilk elemanın anahtar ve değer bilgilerinin nasıl okunduğunu göstermek için forma 2 TextBox yerleştirip yukarıda verilen kodu aşağıdaki gibi düzenledik. 

Dim
iller As New Hashtable()
Dim Liste As IDictionaryEnumerator
Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load 
   iller.Add("01", "Adana"
   iller.Add("06", "Ankara"
   iller.Add("07", "Antalya"
   iller.Add("16", "Bursa"
   iller.Add("33", "Mersin"
   iller.Add("34", "İstanbul"
   iller.Add("35", "İzmir"
   Liste = iller.GetEnumerator() 
   Liste.MoveNext() 
   TextBox1.Text = Liste.Key.ToString() 
   TextBox2.Text = Liste.Value.ToString()
EndSub 

GetEnumerator
() metoduyla hazırlanan enum nesnesinin elemanlarının üzerinde ileri doğru hareket edebilmek için forma “sonraki” adında bir düğme yerleştirip bu düğmenin Click olayını temsil eden metodu aşağıdaki gibi düzenledik. 

Private
Sub sonraki_Click(ByVal sender As System.Object, _
            ByVale As System.EventArgs) Handles sonraki.Click 
  Liste.MoveNext() 
  TextBox1.Text = Liste.Key.ToString() 
  TextBox2.Text = Liste.Value.ToString()
EndSub 

Formdaki düğme tıklanıp bu metot işletildiğinde numaralayıcının bir sonraki elemanın üzerinde gidilir ve Hashtable nesnesinden alınan anahtar bilgisi ilk TextBox’a, değer bilgisi ise 2. TextBox’a aktarılır. 

Hashtable nesnesinin ve dolayısıyla IDictionaryEnumerator tipindeki koleksiyonun son elemanında iken MoveNext() metodu işletilirse hata meydana gelmez ama Key ve Value özellikle­rinin içerikleri okunmak istendiğinde hata meydana gelir. Bunun önüne geçmek için yukarıda verilen kodu aşağıdaki gibi düzenledik. 

Private
Sub sonraki_Click(ByVal sender As System.Object, _
        ByVale As System.EventArgs) Handles sonraki.Click 
  If (Liste.MoveNext()) Then 
    TextBox1.Text = Liste.Key.ToString() 
    TextBox2.Text = Liste.Value.ToString() 
  End If
EndSub 

Tekrarlamak gerekirse; önce IDictionaryEnumerator tipinde bir değişken tanımladık ve GetEnumerator() metodundan yararlanıp Hashtable nesnesinin elemanlarını IDictionaryEnumerator tipindeki numaralayıcıya aktardık. Bu sayede HashTable nesnesinin mevcut elemanlarına kolayca erişim imkânı bulduk. MoveNext() metodu ile üzerine gidilen elemanın anahtar bilgisi Key ve değeri ise Value özelliğine aktarıldığı için bu bilgileri alıp istendiğimiz gibi kullandık. 

GetEnumerator() metodu ile hazırlanıp IDictionaryEnumerator tipindeki değişkene aktarı­lan enum tipinden(yani numaralayıcıdan) geçerli anahtar ve değer bilgilerini bir seferde okumak istiyorsanız Entry özelliğine bakabilirsiniz. Entry özelliğinin nasıl kullanıldığını anlatmak için forma 2 ListBox yerleştirip aşağıda verilen kodu yazdık. 

Private
Sub naklet_Click(ByVal sender As System.Object, _
        ByVale As System.EventArgs) Handles naklet.Click 
   While Liste.MoveNext() 
     ListBox1.Items.Add(Liste.Entry.Key.ToString()) 
     ListBox2.Items.Add(Liste.Entry.Value.ToString()) 
   End While
EndSub 

*bu makaleyi bundan birkaç yıl önce yazmış olsaydık bu button nesnesine “naklet” yerine alışkanlıklarımız gereği “aktar” adını verirdik. Aktar’ı button nesnesi adı olarak kullanmak bir süredir izne tabi olduğu, başka bir deyişle forma yerleştirilen düğmelere “aktar” adını verme hakkı programcılık kitapları yazan birisine ait olduğu için button nesnesine çaresiz “naklet” adını verdik.

VB.NET | Bu yazıya 1 yorum yapılmış. | 29.04.2010 03:45:29

ASP.NET'te HATA YAKALAMAK ve EXCEPTION SINIFLARI


Visual Studio veya Visual Web Developer ile ASP.NET sitesi geliştirilirken konu karşıla­şılması muhtemel hatalar olduğunda akla hemen iki kavram gelmektedir:Hata Ayıklamak ve Hata Yakalamak. Visual Studio size yardım edip bir çok hatayı daha kodu yazarken ayıklamanızı sağlamaktadır. ASP.NET uygulamaları dahilinde karşılaşılar hatalar istenirse metot dahilinde yakalanabilir. Metot dahilinde yakalanmayan hataların sayfa bazında, olmadı site yani uygulama bazında tedbir alınıp yakalanması gerekir. 

Bazen tek tek metotlarla ilgilenmek yerine hataları sayfa bazında yakalamak isteyebilirsiniz. Bu sayede bir sayfada meydana gelen bütün hatalar bir merkezden denetlenir. Sitedeki sayfalarla tek tek ilgilenmek istemeyenler hata yakalamayı uygulama bazında yapabilirler. Öncelikle metot dahilinde hataların nasıl yakalandığından söz edeceğiz. Bu bölümde öncelikle Try-Catch bloklarından söz edilecektir. Devamında Page sınıfının Error olayın­dan ve Page sınıfının ErrorPage niteliğinden söz edilecektir. Bu nakale yakın bir zamanda yazımını tamamladığım ASP.NET kitabından aldığımı biliyorsunuz. Bu kitabın kapağında programlama dili olarak Visal Basic'in seçildiği işaret edildiği için bölümün orijinal halinde programlama dilinin Visual Basic olduğundan tekrar söz edilmedi. Bu nedenle bu makaleyi okuyacak arkadaşlar için açıklama yapmak gerekir: Bu uzun makalede kullanılan kodların dili Visual Basic'tir.

ASP.NET sitelerinde metot dahilinde hata yakalama işlemlerinin nasıl yapıldığını örnek bir olay üzerinde anlatmak istiyo­ruz. Kullanıcıdan bir işlem için TextBox aracılığıyla güncel tarihi istediğinizi varsayalım. TextBox’a tarih olarak değerlendirilemeyecek bilgi girilirse sayfanın sunucuda render edilmesi yani HTML kodu hazırlanması işlemi yarıda kalabilir. Aşağıda verilen metot işletil­di­ğinde sayfadaki ilk TextBox’ın içeriği dönüştürülüp DateTime tipindeki değişkene aktarılır. Devamında bu tarih bilgisi gün, ay ve yıl olarak ayrıştırılıp sayfadaki diğer TextBox’lara aktarılır. 

Protected Sub Ayristir_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Ayristir.Click 
  Dim Tarih As DateTime = DateValue(TextBox1.Text) 
  TextBox2.Text = Tarih.Day 
  TextBox3.Text = Tarih.Month 
  TextBox4.Text = Tarih.Year
End Sub

Bu kodda sayfadaki ilk TextBox’ın mevcut içeriği DateValue() fonksiyonu ile DateTime tipine dönüştürülüp “Tarih” adını verdiğimiz değişkene aktarılmaktadır. Tahmin edeceği­niz gibi TextBox’a tarih olarak değerlendirilmeyecek bilgi girilip bu kodun işletilmesi, daha doğrusu sayfanın postback olması sağla­nırsa hata meydana gelir ve aşağıdakine benzer bir hata mesajı alınır.



Konu hataları metot dahilinde yakalamak olduğunda ilgili sayfanın Visual Studio ile gelen entegre debugger’ın denetiminde mi yoksa debugger’dan bağımsız mı test edildiği önemli­dir. Bu hatayı üzerinde çalıştığımız siteyi entegre hata ayıklayıcısını devre dışı bırakarak test etmek isteyince aldık. Başka bir deyişle bu hatayı Ctrl+F5 tuşlarına basarak siteyi Visual Studio ile gelen web server ile test ederken aldık. 

Şimdi konuyu baştan alalım:Bir sayfaya sahip web sitesi hazırlayıp bu sayfaya 4 TextBox ve bir Button yerleştirdik. Ardından Visual Studio’nun araç çubuğundaki Start Debugging düğmesini tıklayıp siteyi test etmek isteyince aşağıda verilen diyalog kutusu ekrana geldi.



Bu sırada siteye ait ana klasördeki Web.config dosyasındaki debug ayarı “false” idi. Debug ayarı true olsaydı bu diyalog kutusu ekrana gelmezdi. Bu diyalog kutusundaki ilk onay kutusu seçilip OK düğmesi tıklanırsa Web.config dosyasındaki debug ayarı True yapılır ve site entegre debugger’ın kontrolünde test edilmiş olunur. Geliştirme sırasında Debug ayarının True olması önerildiği için bu diyalog kutusundaki ilk radyo düğmesini seçip sitenin entegre hata ayıklayıcısının denetiminde test edilmesini sağladık.

Web.config dosyasındaki Debug ayarı True iken sayfadaki ilk TextBox’a tarih tipine dönüştürülemeyecek bilgi girilip yukarıda verilen kod işletilirse aşağıdaki gibi bir hata mesajı ile karşılaşılır. Yani Visual Studio yukarıdakinden farklı olarak ekrana bir hata sayfası getirmek yerine hataya neden olan kod satırını işaret eder. Bu sayede hataları ayıklamak kolaylaşmaktadır.



Geliştirme sürecinde Web.config dosyasındaki Debug ayarını True olarak ayarlamanız önerilir. Bu siteyi Visual Studio ile gelen gömülü web server ile test etmek yerine bu sayfayı başka bir bilgisayarın başına geçip IIS’ten talep etmiş olsaydık farklı bir hata mesajıyla karşılaşırdık. Aşağıda verilen ekran görüntüsünü incelediğinizde meydana gelen hata hakkında bilgi verilmediğini görürsünüz. Tabi bu sitenin ana sayfasını Intranet’e dahilindeki başka bir bilgisayardan talep edebilmek için bu web sitesinin IIS’in nazarından bir web sitesi veya sanal dizin olarak değerlendirilmiş olması gerekir. Aşağıda verilen ekran görüntüsündeki "bilgisayar1" sözkonusu web sitesinin bulunduğu ve IIS kurulu olan lokal bilgisayarın adıdır.



Bazen web siteleri birden fazla kişi tarafından geliştirilir ve geliştirme sürecinde uzak bir bilgisayarda sayfayı test eden programcı meydana gelen hataların ayrıntılarını görmek iste­yebilir. Bu durumda Web.config dosyasında CustomError tagının mode niteliğini değiştirmeniz gerekir. Web.config dosyasının orijinal halinde CustomError ayarı aşağıdaki gibi olsa bile bu satırların açıklama satırı olması sağlandığı için devre dışıdır. 

<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm"> 
   <error statusCode="403" redirect="NoAccess.htm" /> 
   <error statusCode="404" redirect="FileNotFound.htm" />
</customErrors> 

CustomError konusu bu bölümün sonunda ele alınacaktır. Buna rağmen burada CustomError’dan söz etmemizin nedeni aynı web sitesi üzerinde birden fazla programcı çalıştığında karşılaşılan özel duruma işaret etmektir. Burada Web.config’deki CustomError ayarıyla ilgili olarak yazılanlara şimdilik kayıtsız kalabilirsiniz. 

Şimdilik bu satırların mode’la ilgili olanıyla ilgilendiğimiz için Web.config dosyasındaki bu satırların açıklama satırı olmasını engelleyip özel hata sayfalarıyla ilgili kısmı sildik. Başka bir deyişle Web.config dosyasının hatalarla ilgili kısmını aşağıdaki gibi düzenledik. customErrors tagı Web.config’de system.web’in içinde yer almaktadır. 

<system.web> 
   <customErrors mode="RemoteOnly"> 
   </customErrors>
</system.web> 

Her ne kadar bu ayarın varlığı ile yokluğu bir olsa bile konu hakkında bilgi vermek için bu düzenlemeyi yaptık. Bu şartlarda lokal bilgisayarda yani web sitesinin hazırlandığı bilgisa­yarda hataların ayrıntıları gösterilir ama uzak bilgisayarda bu hatalar görülemez. Yukarıdaki senaryoya göre yani geliştirme safhasında siteyi başka programcılar kendi bilgisayarlarında test etmek istiyorsa ve hataların ayrıntılarını görmelerine izin vermek istiyorsanız Web.config dosyasında mode niteliğini Off olarak ayarlamanız gerekir. 

<customErrors mode="Off">
</customErrors>

Tabi sitenin geliştirilmesi tamamlanıp site yayınlandığında bu ayarın tekrar RemoteOnly yapılması gerekir. Web.config dosyasındaki mode niteliğini On yaparsanız sitenin geliştirildiği local bilgisayarda bile meydana gelen hataların ayrıntıları gösterilmez. Hemen eklemek gerekir; sitenin geliştirildiği bilgisayarda site entegre hata ayıklayıcının denetiminde test edilirken Web.config dosyasındaki customErrors tagındaki mode ayarının bir etkisi yoktur. 

Bu ön bilgilerden sonra sırada meydana gelmesi muhtemel hataların metot dahilinde nasıl yakalandığını anlatmak var. Burada yapılması gereken şudur:Hataya neden olma ihtimali yüksek satırları try bloğuna almak ve meydana gelebilecek hataları catch bloğunda yaka­lamaktır. Bu amaçla yukarıda verilen metodu aşağıdaki gibi düzenledik. 

Protected Sub Ayristir_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Ayristir.Click 
   Dim Tarih As DateTime 
   Try 
     Tarih = DateValue(TextBox1.Text) 
     TextBox2.Text = Tarih.Day 
     TextBox3.Text = Tarih.Month 
     TextBox4.Text = Tarih.Year 
   Catch 
     Response.Write("Girdiğiniz tarih veya saat geçersiz") 
     TextBox1.Focus() 
   End Try
End Sub 

Bu kodda hataya neden olabilmesi muhtemel satırı try bloğuna aldık. Ardından catch bloğunda öngördüğümüz hata meydana geldiğinde yapılacak işlemler için kod yazdık. Catch bloğundaki Focus() metodu sayesinde kontrol tekrar ilgili TextBox’a geçer.   


Kendimizi korumak için izninizle buraya ek bir cümle yazacağız:Yarın bir gün programcılık kitapları yazan birisi hata yakalarken string bilgiyi tarihsel bilgiye dönüştürmeyi ilk ben akıl ettim deyip bizi fikrisini izinsiz kullandığımızı şikayet ederse bu bizim için sürpriz olmayacaktır.
 
Ş
imdi metot dahilinde hata yakalama konusunda 2. bir örnek vereceğiz. Hazırlaya­cağımız örnekte 5 elemanlı bir dizi değişken tanımlayıp For döngüsü ile bu dizi değişkenin eleman­larına bilgi aktaraca­ğız. Dizi değişkene aktaracağımız bilgileri ListBox’tan alacağız. 

Protected Sub naklet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Aktar.Click 
  Dim takimlar(4) As String 
  Dim number_sayi As Integer = ListBox1.Items.Count 
  For i As Integer = 0 To number_sayi - 1 
    takimlar(i) = ListBox1.Items(i).ToString() 
  Next
End Sub 

ListBox, 5 veya daha az elemana sahip iken bu kod sorunsuz olarak çalışır. Ancak ListBox 5’ten fazla elemana sahip iken bu kod işletildiğinde sunucuda sayfayı temsil eden HTML kodunun hazırlanıp istemciye gönderilmesi işlemi yarıda kalır ve aşağıdaki gibi bir hata mesajı alınır.



Bu hatanın meydana gelme nedeni 5 elemanlı dizi değişkenin olmayan 6. elemanına bilgi aktarıl­mak istenmesi­dir. Bu gibi hataların önüne geçip sayfanın HTML karşılığının sunucuda hazırlanması işleminin yarım kalmasını önlemek için aşağıdaki gibi Try-Catch blokları hazırlanabilir. 

Protected Sub naklet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Aktar.Click 
   Dim takimlar(4) As String 
   Dim number As Integer = ListBox1.Items.Count 
   Try 
     For indis As Integer = 0 To number - 1 
       takimlar(indis) = ListBox1.Items(indis).ToString() 
     Next 
  Catch 
    Response.Write("Dizi değişkenin sınırı aşıldı" + Chr(13) + _ 
            Chr(10) + "Yalnızca ilk 5 eleman diziye aktarıldı") 
   End Try 
   For indis As Integer = 0 To takimlar.Length - 1 
     ListBox2.Items.Add(takimlar(indis)) 
   Next
End Sub 

Bu kodda hata meydana geleceğini düşündüğümüz satırları Try bloğuna aldık. Bu satırlarda hata meydana gelmezse kod sorunsuz çalışır ve kodun işletimi Catch bloğundan sonraki ilk satıra geçer. Hata meydana gelirse hatanın meydana geldiği noktada Catch bloğundaki satırlara geçilir. Bu şekilde düzenlenen kodun çalışma­sının kırılmadığını göstermek için sayfaya 2. bir ListBox yerleş­tirdik ve dizi değişkenin elemanlarını 2. ListBox’a aktardık. 

Şimdi yukarıda hazırladığımız koddaki Catch bloğunda değişiklik yapacağız. Hazırlamış olduğumuz örnekte Try bloğundaki satırlar nasıl bir hataya neden olurlarsa olsunlar Catch bloğu işletilir. Catch bloğundaki satırların yalnızca dizi değişkenlerle ilgili olarak indis sorunu yaşandığında işletilmesini isteseydik aşağıdaki gibi düzenleme yapardık. Try bloğun­daki satırlarda çalışma anında yani sayfa sunucuda render edilip HTML kodu hazırlanırken dizi değişkenlerle ilgili hata meydana geldiğinde IndexOfRangeException sınıfının örneği otomatik olarak hazırlanıp Catch bloğuna gönderilir. 

Protected Sub naklet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Aktar.Click 
   Dim takimlar(4) As String 
   Dim adet As Integer = ListBox1.Items.Count
   Try 
      For indis As Integer = 0 To adet - 1 
        takimlar(i) = ListBox1.Items(indis).ToString() 
     Next 
  Catch Hata As IndexOutOfRangeException 
     Response.Write("Meydana gelen hatanın mesajı :" + _ 
       Chr(13) + Chr(10) + Hata.Message) 
   End Try
End Sub 

Bu örnekte sistem tarafından fırlatılan istisnanın yani hazırlanıp Catch bloğuna gönderi­len IndexOutOfRangeException tipindeki nesnenin Message özelliğinin içeriğini sayfaya yazdık. Exception sınıfından türetilen IndexOutOfRangeException sınıfının Message özelliğinden başka Source, HelpLink, StackTrace, TargetSize ve InnerException gibi özellikleri bulunmaktadır. Bu kodu tekrar yorumlamak gerekirse şunlar söylenebilir:Try ve Catch blokları birlikte çalışmakta ve Try bloğundaki herhangi bir satırda hata meydana geldiğinde meydana gelen hataya göre bir Exception nesnesi hazırlanıp kodun işletimi Catch bloğuna geçmektedir. 

Yukarıda verilen kodda Catch bloğuna gönderilen exception nesnesinin IndexOutOfRangeException tipinde olup olmadığını araştırdık. Bu sırada Catch bloğuna gönderilen hata nesnesi IndexOutOfRangeException sınıfının örneği ise Catch bloğuna yazılan satırlar işletilir. Try bloğunda meydana gelen hatadan dolayı Catch bloğuna gönderilen bütün nesneler System’de bulunan Exception sınıfından türetilen sınıfların örnekleridir. Bu kodda dizi değişkenin olmayan bir elemanı üzerinde işlem yapılmak istendiği için Catch bloğuna IndexOutOfRangeException tipinde bir nesne gönderilir. Konu üzerinde düşün­menizi sağlamak için yukarıda verilen ilk örneği aşağıdaki gibi düzen­ledik. 

Protected Sub naklet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Aktar.Click 
  Dim Tarih As DateTime 
  Try 
    Tarih = Convert.ToDateTime(TextBox1.Text) 
  Catch Hata As IndexOutOfRangeException 
    Response.Write("Girdiğiniz tarih veya saat geçersiz") 
    TextBox1.Focus() 
  End Try
End Sub 

Bu şartlarda TextBox’a DateTime tipine dönüştürülemeyecek bilgi girilip bu metot işletil­diğinde Try bloğundaki satırlar hataya neden olur ve işletim Catch bloğunda geçer. Ne var ki Try bloğunda meydana gelen hata bu şatlarda Catch bloğunda yakalanamaz. Çünkü Catch deyimi­nin kullanıldığı satırda IndexOutOfRangeException tipinde değişken tanımlandığı için Catch bloğundaki satırlar işletilmeyip meydana gelen hatadan dolayı sayfanın görüntülen­mesi işlemi durur. Başka bir deyişle Catch bloğunda gönderilecek hata nesnesi IndexOutOfRangeException tipinde olmadığı için bu hata yakalanamaz. 

Şimdi ise hata yakalamanın nasıl yapıldığı anlatılırken ilk akla gelen hatalardan birisi olan sıfıra bölme hata­sını yakalayan bir örnek vereceğiz. Bu amaçla sayfaya 3 TextBox ve 1 düğme yerleştirdik. İlk 2 TextBox’a bilgi girilip “Hesapla” adını verdiğimiz düğme tıklandı­ğında 1. sayı ikinciye bölünüp sonuç 3. TextBox’a yazılacak. 

Tam bu noktada 2004 yılında yayınlanan bir kitabımızda sıfıra bölme hatasından söz ederken aşağıdaki gibi sayi1 ve sayi2 adında 2 değişken tanımladığımız için değişken adı çalma suçlamasıyla dava edildiğimizi bilmenizi isteriz. Aman dikkat:Başkalarına ait değişken adlarını kullanmayınız. Aşağıda verilen kodda bu değişkenlerin tipini byte olarak seçme nedeni şudur:Aynı örnekte hem sıfıra bölme hem de sınır aşımı hatasından söz edebilmektir. Byte yerine Integer’ı tercih etseydik sınır aşımı hatasının meydana gelmesi için TextBox’lara 10 haneli büyük bir tam sayı girmek gerekecekti. Tabi yarın bir gün birileri bu örnekte Integer veri tipi dururken byte tipini tercih ettiğimiz için bizi suçlayabilir. 

Protected Sub Hesapla_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Hesapla.Click 
  Dim sayi1, sayi2 As Byte 
  Dim Netice As Double 
  sayi1 = Convert.ToByte(TextBox1.Text) 
  sayi2 = Convert.ToByte(TextBox2.Text) 
  Netice = sayi1 / sayi2 
  TextBox3.Text = Netice.ToString()
End Sub 

TextBox’lara girilecek sayılar Byte tipindeki değişkenlere aktarılacaktır. Sayfadaki ilk 2 TextBox’a 255’ten küçük bir sayı girilirse bu kod hatasızca çalışır. Kulla­nıcı 2. TextBox’a 0 yazarsa sıfıra bölme hatasının meydana geleceğini ve sayfanın sunucudan istemciye gönderilmeyece­ğini düşünebilirsiniz. Ne var ki bu şartlarda sayfadaki 2. TextBox’a 0 girip bölen sayıyı 0 yapmanız halinde “Netice” değişkenin dolayısıyla 3. TextBox’ın içeriği Infinity olur. Visual Basic derleyicisinin varsayılan ayarlarında Byte bir sayıyı 0’a bölmek hataya neden olmuyor. Bölme işlemini normal bölü(/) yerine ters bölü(\) yani tam bölme operatörü ile yaparsanız 2. TextBox’a 0 yazmanız halinde sıfıra bölme hatası meydana gelir. 

Netice = sayi1 \ sayi2

Aynı şekilde TextBox’lara 255’ten büyük bir değer yazılırsa değişken tipi byte olarak seçildiği için sınır aşımı hatası meydana gelir. Sayfadaki 2. TextBox’a 0 yazıp bu kodu işletmek isteyince sayfa sunucudan talep edilmeyip yapılan hata entegre debugger tarafından aşağıdaki gibi işaret edildi.



Tekrar hatırlatmak gerekirse bu kodun olduğu sayfayı localhost olarak kullandığımız bilgi­sayarda entegre hata ayıklayıcısından bağımsız çalıştırmış olsaydık ekrana aşağıdaki gibi hata sayfası getirilirdi. Tabi bu sayfa yayınlanıp başka bir bilgisayardan talep edilmiş olunsaydı hataya neden olan satır bu şekilde kullanıcıya gösterilmezdi.



Böyle bir hata meydana geldiği zaman sayfanın görüntülenmesinin durmasını engellemek için Try-Catch bloğu hazırladık. Gerçekte bu kod değişik nedenlerden dolayı değişik hata­lara neden olabilir ama öncelikle sıfıra bölme ve taşma hataları ile ilgilenmek istiyoruz.

Protected Sub Hesapla_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Hesapla.Click 
   Dim number1, number2 As Byte 
   Dim Netice As Double 
   Try 
     number1 = Convert.ToByte(TextBox1.Text) 
     number2 = Convert.ToByte(TextBox2.Text) 
     Netice = number1 \ number2 
     TextBox3.Text = Netice.ToString() 
   Catch 
     Response.Write("Hata meydana geldi") 
     TextBox2.Focus() 
   End Try
End Sub
 

Bu kodla ilgili olarak ek açıklama yapacağız: Bu koddaki Try bloğundaki satırlar işletilirken ister taşma hatası ister sıfıra bölme hatası meydana gelsin her şartta kullanıcıya Catch bloğundaki mesaj verilip tekrar ilk TextBox’ın üzerine gidilir. Konu üzerinde düşünmenizi sağlamak için bu kodun Catch bloğunu aşağıdaki gibi düzenledik. 

Catch Hata As System.Exception 
  Dim tip As Object = Hata.GetType() 
  Response.Write("Hata meydana geldi" +Chr(13) + Chr(10) + 
"Meydana gelen hata : " + tip.ToString()) 
  TextBox2.Focus()
End Try
 

Burada yapılan şudur: Catch anahtar kelimesinin bulunduğu satırda Exception tipinde bir değişken tanımladık. Catch deyimi, Try bloğundan gönderilen nesnenin tipinin burada tanımlanan değişkenle aynı tipte olup olmadığına bakar. Gönderilen Exception nesnesi System.Exception tipinde ise Catch bloğundaki satırlar işletilir. Hata nedeniyle Try bloğundan catch bloğuna gönderilen hata nesnesi­nin tipi ister IndexOutOfRangeException olsun ister
OverflowExceptionher şartta catch bloğundaki bu satırlar işletilir. Çünkü IndexOutOfRangeException ve OverflowExceptionsınıfları System.Exception sınıfından türetilmiş sınıflardır. 

Bu kodda bloğun içinde object tipinde bir değişken tanımlayıp catch bloğuna gönderilen nesnenin tipini GetType() metodu ile öğrendik. Catch bloğundaki satırların işletilmesinin nedeni sıfıra bölme iken aşağıdaki gibi bir mesaj alınır.



Gördüğünüz gibi bu sırada meydana gelen hata sıfıra bölme işlemi kaynaklı olduğu için Catch bloğuna DivideByZeroException sınıfının örneği gönderilmiş. TextBox’lardan birisine Convert sınıfının ToByte() metodu ile byte tipine dönüştürülemeyecek bilgi girip ondan sonra bu kodu işletseydik farklı gibi hata mesajı alırdık. İlk 2 TextBox’tan birisine 255’ten daha büyük bir sayı girilirse taşma hatası meydana gelir(
OverflowException).

When Deyimi

Visual Basic kodu içinde Catch bloğunda meydana gelen hatalar yakalarken When deyimi ile bu hataları kategorize edebilirsiniz. Bu konuda bilgi vermek için aşağıda verilen kodu hazırladık. 

Protected Sub islem_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles islem.Click 
  Dim Number As Byte 
  Try 
     Number = Convert.ToByte(TextBox1.Text) 
  Catch hata As OverflowException 
     Response.Write("Hata meydana geldi") 
  End Try
End Sub
 

Tahmin edeceğiniz gibi TextBox’a 255’ten büyük bir değer girilip bu kod işletilirse ToByte() metodunun kullanıldığı satır hataya neden olur ve Catch bloğuna OverflowException sınıfının örneği gönderilir. Buraya kadar sizin için yeni bir şey yoktur. When deyiminin nasıl kulla­nıldığını anlatmak için bu kodu aşağıdaki gibi değiştirdik. 

Protected Sub islem_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles islem.Click 
  Dim Number As Byte 
  Try 
    Number = Convert.ToByte(TextBox1.Text)
  Catch hata As OverflowException When Convert.ToInt16(TextBox1.Text)> 300 
    Response.Write("Girdiğiniz sayı çok büyük") 
  End Try
End Sub
 

Tahmin edeceğiniz gibi Catch bloğundaki satırın işletilmesi veya meydana gelmesi muhte­mel olan OverflowException hatasını yakalamak için TextBox’a 300’den büyük bir sayı girilmelidir. Bu şartlarda TextBox’a 300 ile 256 arası bir değer girilirse meydana gelecek hata yaka­lanmaz ve sayfanın HTML karşılığının sunucuda hazırlanması işlemi yarım kalır. OverflowException hatasını her şartta yakalamak için bu koda ekleme yaptık.
 

Protected Sub islem_Click(ByVal sender As Object, 
ByVal e As System.EventArgs) Handles islem.Click 
  Dim Number As Byte 
  Try 
    Number = Convert.ToByte(TextBox1.Text) 
  Catch hata As OverflowException When Convert.ToInt16(TextBox1.Text)> 300 
    Response.Write("Girdiğiniz sayı çok büyük") 
  Catch hata As OverflowException 
    Response.Write("Hata meydana geldi") 
  End Try
End Sub

Hata Yakalama Sınıfları

ASP.NET sitelerinde hataları yakalarken kullanabileceğiniz çok sayıda sınıf bulunmaktadır. Yukarıda Exception, OverflowException, DivideByZeroException ve IndexOutOfRangeException sınıflarından kısaca söz edildi. Hata yakalama sınıfları­nın işlevleri biraz zor anlaşıldığı için yukarıda sözünü ettiğimiz konulardan tekrar söz edeceğiz. Öncelikle aşağıda verilen kodu incelemenizi istiyoruz. 

Protected Sub Hesapla_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Hesapla.Click 
  Dim Number, Number2 As Byte 
  Dim Netice As Double 
  Try 
    Number1 = Convert.ToByte(TextBox1.Text) 
    Number2 = Convert.ToByte(TextBox2.Text)
    Netice = Number1 \ Number2 
    TextBox3.Text = Netice.ToString() 
  Catch Hata As System.Exception 
    Response.Write("Hata meydana geldi" + Chr(13) + Chr(10) + Hata.ToString()) 
    TextBox2.Focus() 
  End Try
End Sub 

2. TextBox’a sıfır(0) girilip bu kod işletilirse sıfıra bölme hatası meydana gelir(yani Catch bloğuna DivideByZeroException nesnesi gönderilir) ve meydana gelen hata ile ilgili olarak sayfaya hata mesajı yazılır. 

Bu kodda yukarıdaki sayfalarda verilen örneklerden farklı olarak “Hata” adını verdiğimiz bir nesne var. Bildiğiniz gibi nesneleri New anahtar kelimesini kullanıp ilgili sınıfın yapıcı metodundan yararlanarak hazırlanıyorduk. Ancak burada “Hata” adını verdiğimiz System’deki Exception tipindeki nesne otomatik olarak hazırlandı. Bu kodda Catch bloğuna DivideByZeroException nesnesi gönderilmesine ve Catch deyiminin olduğu satırda Exception tipinde değişken tanımlanmasına rağmen hata yakalandı. 

Nasıl ki Object tipindeki değişkenlere istenen tipteki bilgiler aktarılabiliniyorsa bütün hata sınıflarının türetildiği Exception sınıfı tipindeki değişkene istenen hata sınıfının örneği aktarılabilinmektedir. Try bloğundan Catch bloğuna gönderilen DivideByZeroException nesnesi hatasızca dönüştürülüp System.Exception tipindeki değişkene aktarılabildiği için hata yaka­lanıp Catch bloğundaki satırlar işletilebilmektedir. 

Bu sırada sıfıra bölme dışında başka bir hata meydana gelirse ekrana getirilen hata mesajı farklı olur. Sayfa sunucudan talep edilip TextBox’lardan birisine 255’ten büyük bir değer yazılıp bu kod işletilmiş olsaydı aşağıdaki gibi hata mesajı sayfaya yazılırdı.



Şimdi yukarıda verilen kodun Try bloğunda değişiklik yapıp sıfıra bölme hatası meydana geldiğinde başka mesaj, TextBox’a byte tipindeki değişkene sığamayacak büyüklükte bir değer girildi­ğinde sayfaya başka bir mesajın yazılmasını sağlayacağız. 

Protected Sub Hesapla_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Hesapla.Click 
   Dim Number1, Number2 As Byte 
   Dim Netice As Double 
   Try 
     Number1 = Convert.ToByte(TextBox1.Text) 
     Number2 = Convert.ToByte(TextBox2.Text) 
     Netice = Number1 \ Number2 
     TextBox3.Text = Netice.ToString() 
   Catch Hata As System.DivideByZeroException 
     Response.Write("Sıfıra bölme hatası meydana geldi") 
     TextBox2.Focus() 
   Catch Hata As System.OverflowException 
     Response.Write("Büyük sayı girdiniz") 
     TextBox2.Focus() 
   End Try
End Sub 

Bu kodu dikkatle incelerseniz Try bloğundan sonra birden fazla Catch bloğunun olduğunu görürsünüz. Hazırladığımız kodun Catch bloklarını bu şekilde değiştirip TextBox’ların birisine karaktersel bilgi girip bu kodu işletirsek meydana gelecek hata yakalanamaz ve sayfanın HTML karşılığı hazırlanmaz. Çünkü TextBox’a karaktersel bilgi girilip bu kod işletilirse Convert sınıfının ToByte() metodunun kullanıldığı satırlar hataya neden olur. 

Number1 = Convert.ToByte(TextBox1.Text)
Number2 = Convert.ToByte(TextBox2.Text) 

Convert sınıfının bu metodu ile string bilgiler byte tipine dönüştürülmeye çalışılırken meydana gelecek hataları yakalamak istiyorsanız 3. bir Catch bloğu hazırlayabilirsiniz. Convert sınıfının herhangi bir metodu ile dönüştürme yapılırken hata meydana geldiğinde Catch bloğuna FormatException sınıfının örneği gönderilmektedir. 

Aşağıdaki gibi 3 catch bloğu hazırlayıp hem ilk TextBox’a byte tipindeki değişkene aktarıla­mayacak büyüklükte bir sayı girip hem de 2. TextBox’a 0 yazıp sıfıra bölme hatası­nın meydana gelmesini sağlarsanız ilk TextBox’ın içeriği byte değişkene aktarılırken hata meydana geleceği için Catch bloğuna OverflowException nesnesi gönderilir ve 2. Catch bloğu işletilir. 

Catch Hata As System.DivideByZeroException 
  Response.Write("Sıfıra bölme hatası meydana geldi")
Catch Hata As System.OverflowException 
  Response.Write("Büyük sayı girdiniz")
Catch Hata As System.FormatException 
  Response.Write("Yanlış türde bilgi girdiniz")
End Try 

InvalidCastException ve NullReferenceException Sınıfı

Değişik tipteki bilgileri başka bir tipe dönüştür­mek mümkündür.
DirectCast ve
TryCast operatörle­rin­den yararlanıp tip dönüşümleri yapılabilmektedir. Tip dönüştürme işlemleri sırasında sorun olduğunda InvalidCastException hatası meydana gelmektedir. InvalidCastException sınıfını anlatmak için aşağıda verilen kodu hazırladık. 

Dim Nesne As Object = 2008
Try 
  Dim Yil As Integer 
  Yil = DirectCast(Nesne, Integer) 
  Me.Title = Yil.ToString()
Catch Hata As System.InvalidCastException 
  Response.Write("Dönüştürme hatası meydana geldi")
End Try 

Bu kodda “Nesne” adını verdiğimiz object tipindeki değişkenin içeriği rakamlardan meydana geldiği için Unboxing işlemi yapılıp sorunsuz bir şekilde integer tipindeki değiş­kene aktarılır. Object tipindeki değişkenin içeriği integer tipindeki değişkene aktarılırken Unboxing işlemi başarısız olsaydı InvalidCastException hatası ile karşılaşılırdı. Örneğin burada “Nesne” adını verdiğimiz Object tipindeki değişken Integer değişkenlerin kapasite­lerini aşan büyüklükte bir sayı aktarsaydık Catch bloğuna InvalidCastException tipinde bir nesne gönderilirdi. 

Nothing
yapılmış bir nesnenin özelliklerini kullanmak isterseniz NullReferenceException hatası ile karşılaşılır. Bu hata ile nasıl karşılaşıldığını göstermek için bir Bitmap nesnesi hazırlayıp Nothing yaptıktan sonra Width ve Height özelliklerini kullanmayı denedik. 

Dim Resim As System.Drawing.Bitmap
Dim dosya As String = Server.MapPath("jazz.jpg")
Try 
  Resim = New System.Drawing.Bitmap(dosya) 
  Resim = Nothing 
  Dim i As Integer = Resim.Width 
  Dim j As Integer = Resim.Height
Catch Hata As NullReferenceException 
  Response.Write("Nesne Nothing yapılmış")
End Try 

Throw Deyimi İle Exception Nesnesi Hazırlamak 

Şimdiye kadar anlatılanlardan çıkarmış olabileceğiniz gibi herhangi bir nedenden dolayı bir metot dahilinde hata meydana geldiğinde karşılaşılan hataya uygun olarak ilgili hata sınıfının örneği hazırlanıp gönderilmektedir. Bizim yaptığı­mız, fırlatılan nesnenin hangi sınıfın örneği olduğunu öğrenip ona göre kullanıcıya mesaj verip tedbir almaktı. Burada asıl vurgulamak istediğimiz konu, sistem bir exception fırlat­tığında bu Exception’a kaynaklık edecek sınıfı kendisinin seçtiğidir. 

Şimdi Throw deyiminden yararlanıp kendimiz bir Exception nesnesi hazırlayıp Catch bloğuna göndereceğiz. Bu amaçla kullanıcıdan gün, ay ve yıl bilgilerini isteyip bunları birleştirip tarih bilgisi elde edece­ğiz. Bu amaçla sayfaya 4 TextBox yerleştirip aşağıda verilen kodu hazırladık. 

Protected Sub Birleştir_Click(ByVal sender As Object,
ByVal e As EventArgs) Handles Birleştir.Click 
  Dim Tarih As DateTime 
  Dim gun As Integer = Convert.ToInt16(TextBox1.Text) 
  Dim ay As Integer = Convert.ToInt16(TextBox2.Text) 
  Dim yil As Integer = Convert.ToInt16(TextBox3.Text) 
  Tarih = DateSerial(yil, ay, gun) 
  TextBox4.Text = Tarih.ToShortDateString()
End Sub 

Kullanıcı TextBox’lara uygun değerler girdiğinde bu kod sorunsuz çalışır. Ancak gün bilgisi olarak 31’den, ay bilgisi olarak 12’den büyük bir değer girdiğinde gün, ay ve yıl bilgileri birleştirilip tarih elde edilmek istendiğinde hata meydana gelir. 

Yanlış bilgi girişinden dolayı sayfadaki kırılmayı önlemek için yukarıda yapıldığı gibi Try-Catch bloğu hazırlayabilir veya kullanıcının girdiği bilgileri DateSerial() metoduna parametre olarak vermeden önce kontrol edebilirsiniz. Pratik değeri olmasa bile aşağıda verilen kod hatasız çalışır ve DateSerial() metoduna parametre olarak verilecek ay ve gün bilgilerinin yanlış olması engellenir. 

Protected Sub Birleştir_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Birleştir.Click 
  Dim Tarih As DateTime 
  Dim gun As Integer = Convert.ToInt16(TextBox1.Text) 
  Dim ay As Integer = Convert.ToInt16(TextBox2.Text) 
  Dim yil As Integer = Convert.ToInt16(TextBox3.Text) 
  If gun > 31 Or ay > 12 Then 
    Try 
       Throw New OverflowException("Yanlış bilgi girdiniz") 
     Catch Hata As Exception 
       Response.Write(Hata.Message) 
       TextBox1.Focus() 
     End Try 
  End If 
  Tarih = DateSerial(yil, ay, gun) 
  TextBox4.Text = Tarih.ToShortDateString()
End Sub 

Kullanıcı ay veya gün bilgilerinde sınırı aştığında catch bloğu işletilir. try bloğunda şimdiye kadar yapılanlardan farklı olarak throw deyimi ile OverflowException nesnesi hazırlan­maktadır. Burada throw deyimi ile Exception nesnesi hazırlandığı için sıra catch bloğunu işletmeye gelir. 

Catch bloğunda ise kullanıcıya mesaj verildikten sonra imleç ilk TextBox’a alınıp kullanı­cıya girdiği bilgileri düzeltme şansı verilir. Throw deyimi ile OverflowException nesnesi yerine başka bir Exception nesnesi hazırlayabilirdik. Hemen hatırlatmak gerekirse, throw deyimi ilgili exception sınıfının örneğini alma işlemi daha çok kendi özel hata sınıflarını hazırlayan programcılar gerek duymaktadır. 

Finally Bloğu

B
azen herhangi bir nedenden dolayı yarı yolda vazgeçilen bir işlemden dolayı geride bazı kalıntılar kalır. Bu gibi durumlar kaynak israfına neden olur. Bu konuda bilgi vermek için aşağıda verilen sayfayı hazırladık.



Bu sayfadaki “Kaydet” başlıklı düğme için aşağıda verilen kodu hazırladık. Bu sayfa görüntülenip TextBox’lara uygun bilgi girildiği sürece bu kod hatasız çalışır ve metodun sonunda Stream kapatıldığı için text dosyası serbest bırakılır. Dolayısıyla başka bir ziyaretçi bu sayfa aracılığıyla dosyaya bilgi yazabilir. 

Protected Sub Kaydet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Kaydet.Click 
  Dim akis As System.IO.FileStream 
  akis=New System.IO.FileStream(Server.MapPath("dosya.txt"), IO.FileMode.Append) 
  Dim yazici As System.IO.StreamWriter 
  yazici = New System.IO.StreamWriter(akis) 
  Dim sira_no As Integer = Convert.ToInt16(TextBox1.Text) 
  Dim Tarih As DateTime = Convert.ToDateTime(TextBox2.Text) 
  Dim aciklama As String = TextBox3.Text 
  yazici.WriteLine(sira_no.ToString()) 
  yazici.WriteLine(Tarih.ToString()) 
  yazici.WriteLine(aciklama) 
  yazici.Close() 
  akis.Close()
End Sub 

Ancak kullanıcı TextBox’lara uygun olmayan yani Integer ve DateTime tipine dönüştü­rülmeyecek bilgi girerse hata meydana gelir. Hata meydana geldiğinde sayfanın görüntü­lenmesine devam edilmesi için bu kodu aşağıda gibi düzenledik. 

P
rotected Sub Kaydet_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Kaydet.Click 
  Dim akis As System.IO.FileStream 
  akis = New System.IO.FileStream(Server.MapPath(|
"dosya.txt"), IO.FileMode.Append) 
  Dim yazici As System.IO.StreamWriter 
  yazici = New System.IO.StreamWriter(akis) 
  Try 
    Dim sira_no As Integer = Convert.ToInt16(TextBox1.Text) 
    Dim Tarih As DateTime = Convert.ToDateTime(TextBox2.Text) 
    Dim aciklama As String = TextBox3.Text 
    yazici.WriteLine(sira_no.ToString()) 
    yazici.WriteLine(Tarih.ToString()) 
    yazici.WriteLine(aciklama) 
    yazici.Close() 
    akis.Close() 
  Catch 
    Response.Write("Hatalı bilgi girdiğiniz") 
  End Try
End Sub 

Kullanıcı uygun olmayan bilgi girerse catch bloğundaki satır işletilir ve kullanıcıya bu konuda bilgi verilir. Ancak bu durumda “yazici” ve “akis” adını verdiğimiz stream nesneleri ilgili text dosyasını tutmaya devam ederler. Çünkü bu stream nesnelerini kapatan satırları try bloğuna yazmıştık. 

Hata meydana gelsin veya gelmesin mutlaka işletilmesini istediğiniz satırlar varsa bu satırları Finally bloğuna yazmalısınız. Finally bloğunun nasıl kullanıldığını anlatmak için yukarıda verilen kodu aşağıdaki gibi değiştirdik. 

Protected Sub Kaydet_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles Kaydet.Click 
   Dim akis As System.IO.FileStream 
   akis=New System.IO.FileStream(Server.MapPath("dosya.txt"), IO.FileMode.Append) 
   Dim yazici As System.IO.StreamWriter 
   yazici = New System.IO.StreamWriter(akis) 
   Try 
      Dim sira_no As Integer = Convert.ToInt16(TextBox1.Text) 
      Dim Tarih As DateTime = Convert.ToDateTime(TextBox2.Text) 
      Dim aciklama As String = TextBox3.Text 
      yazici.WriteLine(sira_no.ToString()) 
      yazici.WriteLine(Tarih.ToString()) 
      yazici.WriteLine(aciklama) 
   Catch 
      Response.Write("Hatalı bilgi girdiğiniz") 
   Finally 
     yazici.Close() 
     akis.Close() 
  End Try
End Sub 

Gerek duyarsanız birden fazla Catch bloğu ve/veya iç-içe Try-Catch bloğu hazırlayabilirsi­niz. Söz konusu hatanın birden fazla nedenden kaynaklanması ihtimali varsa try bloğu için birden fazla catch hazırlanabilir.

Entegre Hata Ayıklayıcısı ve Exception Sınıfları 

Çok kullanılan hata yakalama sınıflarından biraz söz ettikten sonra mevcut hata sınıflarıyla entegre hata ayıklayıcının ilişkisinden söz edeceğiz. Bu amaçla Visual Studio'nun Debug menü­sünden komut verip ekrana Exceptions diyalog kutusunu getirdik.



Bu diyalog kutusunda Visual Studio ile hazırlanan web uygulamaları dahilinde yararlana­bileceğiniz Exception sınıflarıyla, entegre hata ayıklayıcısı arasındaki ilişki ayarlanmaktadır. Bu konuda bilgi vermek için sayfaya bir TextBox ve düğme yerleştirip düğmenin Click olayı için aşağıda verilen kodu hazırladık. 

Private Sub islem_Click(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles islem.Click 
   Dim number As Byte 
   Try 
     number = Convert.ToByte(TextBox1.Text) 
   Catch hata As OverflowException 
     Response.Write("Hata meydana geldi") 
   End Try
End Sub 

Tahmin edeceğiniz gibi Start Debugging komutu verilip sayfa sunucudan talep edilip TextBox’a 255’ten büyük bir sayı girilmesi halinde hata meydana gelir. Bu hatadan dolayı OverflowException tipinde nesne hazırlanıp catch bloğuna gönderilir. 

Şimdi öyle bir ayarlama yapacağız ki site Start Debbuging komutu verilip Visual Studio ile gelen web server ile test edilirken Catch bloğundaki satırların işletilmesi yerine uygulama­nın çalışmasının kırılmasını sağlayacağız. Bu amaçla Exceptions diyalog kutusunda önce Common Language Runtime Exceptions adlı exception sınıfı grubuna ait namespace’lerin listelenme­sini sağladık. Ardından System adlı namespace’te yer alan OverflowException hata sınıfını bulduk.



Verilen ekran görüntüsünde tespit edebileceğiniz gibi bu hata sınıfı için Thrown özelliği pasif iken User-unchandled özelliği aktif durumdadır. Başka bir deyişle kodun herhangi bir yerinde meydana gelebilecek ve OverflowException sınıfının örneğinin fırlatılmasına neden olacak hata yakalanmazsa sayfanın sunucuda hazırlanıp istemciye gönderilmesi işlemi tamamlanmaz.

Yukarıda verilen kodda TextBox’a 255’ten büyük bir değer girilirse, dolayısıyla byte değiş­kene 255’ten büyük bir sayı aktarılmak istenirse meydana gelebilecek hata yakalandığı için sayfanın render edilmesi işlemi yarım kalmaz. Başka bir deyişle mevcut ayarlarda Byte tipindeki değişkene 255’ten büyük bir değer aktarılmak istenirse ve OverflowException hatasını yakalayan bir Catch bloğu yoksa talep edilen veya PostBack olan sayfa istemciye gönderilmez. Benzer şekilde .NET Framework ile gelen sınıfla­rın hemen hepsi için User-unhanded sütunundaki onay kutusu seçili durumda olduğu için yakalanmayan bütün hatalar Debug modda uygulamanın kırılmasına neden olurlar. 

Bu diyalog kutusunda System’de yer alan OverflowException sınıfının User-unchandled özelliği aktif durumda iken ayrıca Thrown sütunundaki onay kutusunu aktif duruma getir­dikten sonra OK düğmesi ile bu diyalog kutusunu kapattık. Ardından yukarıda verilen kodu aşağıdaki gibi düzenledik. 

Private Sub islem_Click(ByVal sender As System.Object, 
ByVal e As System.EventArgs) Handles islem.Click 
  Dim number As Byte 
  Try 
    number = Convert.ToByte(TextBox1.Text) 
    If number = 100 Then 
      Throw New OverflowException 
    End If 
  Catch hata As OverflowException 
    Response.Write("Hata meydana geldi veya 100 girdiniz") 
  End Try
End Sub

Önce bu kodu biraz yorumlayalım: TextBox’a 255’ten büyük bir değer girilip bu kod işleti­lirse ToByte() metodunun kullanıldığı satır hataya neden olur ve Catch bloğuna OverflowException sınıfının örneği gönderilir. TextBox’a 100 sayısı girilip bu kod işletildi­ğinde ise Try bloğundaki karşılaştırma True olacağı için Catch bloğuna yine OverflowException sınıfının örneği gönderilir. 

Tekrar etmek gerekirse ister TextBox’a 100 ister 255’ten büyük bir sayı yazılsın OverflowException sınıfının örneği Catch bloğuna fırlatılır. Bu şartlarda yani Exceptions diyalog kutusunda OverflowException sınıfının Thrown özelliği seçili durumda iken site Start Debugging komutu ile test edildiğinde TextBox’a ister 100 girilsin ister 255’ten büyük sayı uygulamanın çalışması kırılır. Çünkü Exceptions diyalog kutusunda yaptığımız ayarla­manın anlamı şudur: Her ne nedenden olursa olsun ister direk Throw deyimi kullanılsın ister yapılmak istenen işlem sonucu OverflowException hatası üretilirse ilgili sayfanın HTML kodunun hazırlanıp istemciye gönderilmesi işlemi yarıda kalır. Tabi siteyi yayınlayıp başka bir bilgisayarda test ederseniz TextBox’a ister 255’ten büyük sayı girilsin ister 100 girilsin Catch bloğundaki satırlar işletilir ve uygulamanın çalışması kırılmaz. 

Sayfa Seviyesinde Hata Yakalamak 

Bu bölümde şimdiye kadar Page sınıfının mirasçısı sayfalarda ve kendi yazacağınız class’larda metotlar dahilinde meydana gelmesi muhtemel hataları nasıl yakalayabileceği­nizden söz ettik. Şimdi gelelim sayfa bazında hataların nasıl yakalandığını anlatmaya. ASP.NET sitesini hazırlayan programcı sayfa ve sınıflarda hazırladığı metotlarda meydana gelmesi muhtemel hataları öngörüp her metot için hata yakalama konusunda tedbir almış olsa bile bazı öngörülemeyen hatalar olabilir. 

Bu konuda bilgi vermek için yukarıdaki sayfalarda sözünü ettiğimiz sıfıra bölme hatasından tekrar söz edeceğiz. Her ne kadar sıfıra bölme hatasından söz etme hakkı(!) yerli bir yazara ait olsa bile bu yasağı burada bir süreliğine deleceğiz. 

Protected Sub hesapla_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles hesapla.Click 
  Dim Number, Number2 As Byte 
  Dim Netice As Double 
  Number1 = Convert.ToByte(TextBox1.Text) 
  Number2 = Convert.ToByte(TextBox2.Text) 
  Netice = Number1 \ Number2 
  TextBox3.Text = Netice.ToString()
End Sub 

Bu metotta meydana gelmesi muhtemel hatalardan birisi olan sıfıra bölme hatasını metot dahilinde Try-Catch bloğuyla yakalamadığımız için bu kod işletilmek üzere sayfa PostBack edildiğinde sayfanın HTML karşılığı hazırlanmaz. Bu hatanın önüne nasıl geçildiğinden söz etmek için hataya neden olan metodun bulunduğu sayfanın(yani Page sınıfının mirasçısı sınıf) Error olayını temsil edecek metodu aşağıdaki gibi düzenledik. Bu metotta text dosya­sına hata meydana geldiği bilgisi yazılmaktadır. Başka bir deyişle karşılaşılan hata log’lanmaktadır. Kod uzamasın diye text dosyasına meydana gelen hatayla ilgili açıklayıcı bilgi yazmadık. 

Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
  Dim akis As System.IO.FileStream 
  akis=New System.IO.FileStream(Server.MapPath("hatalar.txt"), IO.FileMode.Append) 
  Dim yazici As System.IO.StreamWriter 
  yazici = New System.IO.StreamWriter(akis) 
  Dim Tarih As DateTime = Now 
  yazici.WriteLine(Tarih.ToString()) 
  yazici.WriteLine("Hata meydana geldi") 
  yazici.Close()
End Sub 

Her ne kadar bu metoda Page_Error() adını vermiş olsak bile sizler istediğiniz adı verebilir­siniz. İleriki günlerde bu kitap yayınlandığında programcılık kitapları yazan birisi “Page_Error” metodunu kullanmayı ilk ben akıl ettim, dolayısıyla çalıntı yapılmıştır diye bizi suçlamasını engellemek için bu metoda bir de aşağıdaki gibi farklı bir ad verdik. 

Protected Sub Hatalarimla_Sev_Beni(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
  Dim akis As System.IO.FileStream 
  akis=New System.IO.FileStream(Server.MapPath("hatalar.txt"), IO.FileMode.Append) 
  Dim yazici As System.IO.StreamWriter 
  yazici = New System.IO.StreamWriter(akis) 
  Dim Tarih As DateTime = Now 
  yazici.WriteLine(Tarih.ToString()) 
  yazici.WriteLine("Hata meydana geldi") 
  yazici.Close()
End Sub 

Bu metoda ait ilk satırda Handles deyimi ile metodun hangi nesnenin hangi olayını temsil ettiği işaret edildiği için metoda verilen adın önemi yoktur. Ancak Error olayını temsilen hazırlanan metodun Object ve EventArgs tipinde iki parametreye sahip olması ve geriye bilgi göndermemesi yani Sub tipi metot olması şarttır.

Üzerinde çalıştığınız sayfanın olaylarını temsil edecek metotlar hazırlarken kod penceresin­deki Class Name liste kutusunda Page Events seçili iken Method Name liste kutusunda ilgilendiğiniz olayı seçerseniz Visual Studio’nun sizin için metodu hazırlayacağını biliyorsu­nuz.



Yukarıda verilen yani sayfanın Error olayını temsil eden metot, sayfa dahilinde programcı tarafından hazırlanan metotlarda yakalanmayan herhangi bir hata meydana geldiğinde işletilir. Örnek olması için sayfanın Error olayını temsilen hazırladığımız bu metotta StreamWriter sınıfıyla text dosyasına mesaj yazıldıktan klasik hata sayfası ekrana getirilir. Sayfa entegre hata ayıklayıcının eşliğinde test ediliyorsa Error olayını temsil eden bu metot işletildikten sonra sayfanın görüntülenmesi işlemi yarım kalır ve hataya neden satır işaret edilir.

B
u şartlarda yani sayfanın Error olayını temsil eden böyle bir metot varken, sayfa yayınla­nıp herhangi bir istemciden görüntülenirse neler olur ona bakmak gerekir. Bu sırada bu sayfa dahilinde bir hata meydana gelir ve bu hata ilgili metot dahilinde yakalanmazsa Error olayı meydana gelir. Bu sınıfın yani sayfanın bu olayla ilgili mevcut Page_Error() metodu işletilip text dosyasına hatayla ilgili bilgi yazılır. Devamında yine ekrana klasik hata raporu getirilir ki bu raporda kullanıcı için anlaşılır özel bilgi yoktur. Konunun diğer ayrıntılarını ortaya çıkarmak için yukarıda verilen Page_Error() adlı metotta bazı değişiklikler yaptık. 

Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
 Dim akis As System.IO.FileStream 
 akis=New System.IO.FileStream(Server.MapPath("hatalar.txt"), IO.FileMode.Append) 
 Dim yazici As System.IO.StreamWriter 
 yazici = New System.IO.StreamWriter(akis) 
 Dim son_hata As System.Exception 
 son_hata = Server.GetLastError() 
 Dim Tarih As DateTime = Now 
 yazici.WriteLine(Tarih.ToString()) 
 yazici.WriteLine(son_hata.ToString()) 
 yazici.Close()
End Sub 

Metoda sonradan eklediğimiz 2 satırda önce Exception tipinde bir değişken tanımladık. Devamında HttpServerUtility sınıfının örneği olan Server nesnesinin GetLastError() metoduyla web sitesiyle ilgili olarak meydana gelen son hatadan dolayı hazırlanan Exception nesnesini bu değişkene aktardık. En son olarak bu nesnenin içeriğini text dosya­sına yazdık. Aşağıda verilen ekran görüntüsü sayfada sıfıra bölme hatasının meydana gel­mesini sağlayıp Page_Error() metodunun işletilmesine neden olduktan sonra aldık.



Tabi burada yapıldığı gibi meydana gelen en son hatayla ilgili bilgilerin tümü yerine Message özelliğinin içeriğiyle ilgilenebilirdik. Sayfanın Error olayını temsil eden metodun bu şekilde düzenlenmesinin kullanıcıya bir yararı yoktur. Çünkü burada meydana gelen hata loglanmakla yani hatayla ilgili bilgiler text dosyasına yazılmakla birlikte kullanıcı yine klasik hata mesajıyla karşılaşmaktadır. 

Meydana gelen hatayla ilgili bilgileri sayfaya yazıp kullanıcıya bilgi vermek istiyorsanız meydana gelen en son hatayı HttpServerUtility tipindeki Server nesnesinin
ClearError() metoduyla silmeniz gerekir. Bu metodu nasıl kullandığımızı aşağıda görebilirsiniz. 

Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
  Dim son_hata As System.Exception 
  son_hata = Server.GetLastError() 
  Server.ClearError() 
  Response.Write("Hata meydana geldi" + "<br/>") 
  Response.Write("Meydana gelen hata :" + son_hata.Message)
End Sub 

Kodu kısaltmak için meydana gelen hatayla ilgili olarak text dosyasına bilgi yazan satırları kaldırdık. Bu kodda GetLastError() metoduyla meydana gelen en son hata nesnesini elde ettikten sonra ClearError() metoduyla bu hatanın Server nesnesinden silinmesini sağladık. Hata ClearError() metoduyla silindikten sonra tarayıcı penceresine klasik hata sayfası gelmez. Aşağıda verilen ekran görüntüsü sıfıra bölme hatasının meydana gelmesini sağlanıp Page_Error() adlı metot işletildikten sonra alındı.



Programcılar genellikle Page_Error() metodunda yakaladıkları hatanın etkisini ortadan kaldırırken özel bir hata sayfası hazırlıyorlar. Bu düşünceyle sitede “hata.aspx” adında kısıtlı özelliklere sahip bir sayfa hazırlayıp bu sayfanın Load olayını temsil eden metodu aşağıdaki gibi düzenledik.



Şimdi öyle bir ayarlama yapmalıyız ki ana sayfasındaki metotlar dahilinde yakalanmayan herhangi bir hata meydana geldiğinde “hata.aspx” adını verdiğimiz sayfa görüntülensin. Bu ayarlama yukarıda verilen Page_Error() metodunda yapılabilir. Bu düşünceyle sitenin ana sayfasının bu metodunu aşağıdaki gibi düzenledik. Hataya neden olan kodları kısaltmak için hata fırlatma işlemini Throw deyimiyle yaptık. 

Partial Class _Default 
    Inherits System.Web.UI.Page 
 Protected Sub islem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles islem.Click 
  Throw New DivideByZeroException() 
 End Sub 

 Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
    Response.Redirect("hata.aspx") 
 End Sub
End Class 

Bu şartlarda sitenin ana sayfasındaki “islem” adlı düğme tıklandığında Throw deyimiyle yani anahtar kelimesiyle DivideByZeroException sınıfının örneği alınıp ortama fırlatılır. Bu hatayı yakalayan bir catch bloğu olmadığına göre sitenin ana sayfasıyla ilgili olarak Error olayı meydana gelir. Bu sırada Error olayını temsil eden metot olduğu için işletilir. Bu metotta yani Error olayını temsil eden metotta Response nesnesinin Redirect() metoduyla “hata.aspx” adını verdiğimiz sayfaya geçildiği için sitenin ana sayfası görüntülenip bu sayfa­daki “islem” düğmesi tıklanırsa aşağıdaki gibi bir sonuç alınır. Aşağıda verilen ekran görün­tüsü alındığı sırada “hata.aspx” adını verdiğimiz sayfa görüntüleniyordu.



Şimdi “hata.aspx” adını verdiğimiz bu sayfanın Load olayını temsil eden metodu geliştirip sitenin ana sayfasında meydana gelen hata hakkında kullanıcıya bilgi vermeyi deneyeceğiz. Tekrar etmek gerekirse; yukarıda sitenin ana sayfasında sayfanın Error olayıyla ilgili Page_Error() metodunda Redirect() metoduyla “hata.aspx” adını verdiğimiz sayfayı talep ettik. Bu durumda hata.aspx sayfasında bir önceki sayfada karşılaşılan hatayla ilgili bilgi artık yoktur. Bu nedenle kullanıcıya sunulmak istenen bilgiler bir şekilde burada “hata.aspx” adını verdiğimiz sayfaya ulaştırılmalıdır. Bu amaçla ana sayfanın yukarıda verdiğimiz Error_Page() metodunu aşağıdaki gibi düzenledik. 

Partial Class _Default 
   Inherits System.Web.UI.Page 
  Protected Sub islem_Click(ByVal sender As Object, ByVal e As EventArgs) Handles hesapla.Click 
    Throw New DivideByZeroException() 
  End Sub 

 
Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
    Dim hata_nesnesi As New Exception() 
    hata_nesnesi = Server.GetLastError() 
    Dim orijinal_mesaj As String 
    orijinal_mesaj = hata_nesnesi.Message 
    Response.Redirect("hata.aspx?mesaj=" + orijinal_mesaj)  
 End Sub
End Class 

Page_Error() metodunun bu yeni halinde kullanıcıya verilmek istenen bilgiyi yani ana sayfaya karşılaşılan hatanın orijinal mesajını “hata.aspx” adlı sayfaya QueryString’le gönderdik. Ortama fırlatılan hatanın yakalandığı metodu bu şekilde düzenledikten sonra kullanıcıya bilgi verilen “hata.aspx” adlı sayfanın Load olayıyla ilgili metodu aşağıdaki gibi düzenledik. Tabi sizler içinde bulunulan şartlara göre kullanıcıyı yönlendiren ve ayrıntılı bilgi veren bir sayfa hazırlayabilirsiniz. 

Partial Class hata 
  Inherits System.Web.UI.Page 
  Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load 
    Response.Write("Hata meydana geldi" + "<br>") 
    Response.Write(Request.QueryString("mesaj")) 
  End Sub
End Class 

Hata Yakalama Noktalarının Öncelik Sıraları 

Konunun kolay kavranmasını sağlamak için yukarıda sayfa seviyesinde yapılan hata yaka­lamayla ilgili olarak anlatılanları özetleyeceğiz. Ancak bu kez hazırlayacağımız sayfayı IIS’ten yararlanarak test edeceğiz. Bu sayede hata yakalama noktalarının öncelik sıralarını net olarak görmüş olacağız. Bu amaçla yeni bir site hazırlayıp ana sayfasına Button nesnesi yerleştirip bu button nesnesinin Click olayını temsilen aşağıdaki gibi bir metot hazırladık. 

Protected Sub islem_Click(ByVal sender As Object, 
ByVal e As EventArgs) Handles islem.Click 
  Throw New DivideByZeroException()
End Sub 

Bu metotta Throw anahtar kelimesiyle DivideByZeroException nesnesi hazırlanıp ortama fırlatılmaktadır. Bu metodu hazırladıktan sonra Visual Studio’nun Build menüsünden komut verip aşağıda verilen Publish Web Site diyalog kutusunu ekrana getirdik.



Bu diyalog kutusundan yararlanarak üzerinde çalıştığımız siteyi kullandığımız bilgisayarda “\InetPub\wwwroot” klasörüne aktardık. Başka bir deyişle sadece ana sayfasına bir Button nesnesi yerleştirdiğimiz sitenin IIS’in nazarında Default Web Site olarak değerlendirilme­sini sağladık. Bu web sitesini hazırlayıp yayınladığımız bilgisayarın adı SERVER’dı. Siteyi yayınlama işleminden sonra başka bir bilgisayarın başına geçilip bu sırada sunucu görevi yapan IIS’ten bu sitenin ana sayfası talep edilip sayfadaki düğme tıklandığında aşağıdaki gibi bir hata sayfası ile karşılaşılır.



Hata yakalama konusunu özetlemek amacıyla hazırladığımız bu web sitesinin ana sayfasındaki düğme tıklandığında ortama sıfıra bölme hatası fırlatılıp sunucunun browser penceresine bu sayfayı göndermesi sağlanmaktadır. Bu hata sayfası IIS ile birlikte gelen standart sayfa­lardan birisidir. Bu sayfada kullanıcıya karşılaştığı hata hakkında açıklayıcı bilgi verilme­mektedir. Throw deyimi ile sıfıra bölme hatasını meydana getirmek yerine olmayan bir sayfaya gitmek istemiş olsaydık sunucu başka bir hata sayfasını istemciye gönderirdi.

Bu hatayı yani Throw deyimi ile ürettiğimiz hatayı try-catch blokları ile yakalamadığımız için öncelikle sayfanın Error olayını temsil eden bir metodunun olup olmadığı araştırılır. Bu sayfayla ilgili olarak Error olayı meydana geldiğinde işletilecek bir metot henüz olmadığı için bu kez Application nesnesinin Error olayıyla ilgili bir metodun olup olmadığı araştırı­lır. Üzerinde çalıştığımız sitede Application_Error adında ve uygulamanın Error olayını temsil eden bir metot henüz olmadığı için son çare olarak IIS ile gelen standart hata sayfası istemciye gönderildi.

B
u anlatılanları bir cümle ile özetlemek gerekirse; ASP.NET sitelerinde hata yakalama konusunda herhangi bir hazırlık yapılmadığında hatalı bir durumla karşılaşıldığında IIS ile gelen standart hata sayfalarından birisi istemciye gönderilmektedir. 

Bu bölümün ilk sayfalarında hata yakalama konusunu try-catch bloklarından itibaren anlatmaya başladık. Sitedeki sayfalarda meydana gelen hataları birden fazla yerde yakala­mak mümkün olduğu için henüz yolun başındakiler için bu çeşitlilik kafa karıştırmaktadır. Herhangi bir sayfada herhangi bir hata meydana geldiğinde gelişen olayları anlatmak için Throw deyimi ile Exception nesnesi ürettikten sonra aşağıdaki gibi bir Global.asax dosyası hazırladık. Bu dosyanın nasıl hazırlandığını önceki konulardan biliyorsunuz.



Visual Studio bu dosyada çok kullanılan 5 olay için 5 metodu otomatik olarak hazırladığı ve bu metotlardan birisi uygulamanın Error olayını temsil eden metot olduğu için Application_Error() metoduna sonradan 2 satır yazdık. Bu metoda sonradan eklediğimiz ilk satırda web sitesi dahilinde meydana gelen en son hatayı temizledik. Çünkü bu metotta en son meydana gelen hata devreden çıkarılmazsa hata yakalanmamış gibi browser pence­resine standart hata sayfalarından birisi getirilir. Bu sırada yani hatayı devre dışı yaptıktan sonra meydana gelen en son hatayla ilgili bilgileri loglayabilir yani bir dosyaya yazabilirsiniz. 

Global.asax dosyası aracılığı ile uygulama dahilinde en son meydana gelen hatayı yakalayıp silmekten söz ettikten sonra şimdi sırada Web.config dosyasıyla uygulamadaki herhangi bir sayfada meydana gelen hataların nasıl yakaladığı konusu var. Bu amaçla Web.config dosya­sında CustomErrors tagına yer verip uygulamadaki herhangi bir sayfada herhangi bir hata meydana geldiğinde “genel_hata.aspx” adlı sayfaya geçilmesini istediğimizi belirttik. Bu bölümün ileriki sayfalarında CustomErrors tagı hakkında bilgi verilecektir.



Web.config dosyasında customErrors tagını bu şekilde düzenledikten sitenin ana klasörüne “genel_hata.aspx” adında bir sayfa yerleştirdik. Bu sayfanın Load olayını temsil eden metodu aşağıdaki gibi düzenledik.



Bu şartlarda sitenin ana sayfası sunucudan talep edilip ana sayfadaki düğme tıklanıp Throw düğmesiyle hata üretilirse öncelik Global.aspx dosyasındaki Application_Error() meto­dunda olduğu için Web.config dosyası aracılığıyla işaret edilen bu sayfaya geçilemez. Çünkü Global.aspx’deki Application_Error() metodu dahilinde ClearError() metoduyla uygula­manın herhangi bir sayfasında meydana gelen en son hatayı sildik. Eğer hem Global.asax dosyasında dahilinde Application_Error() metoduna yer veriyor hem de Web.config dosyası dahilinde CustomErrors tagıyla mevcut bir sayfayı işaret ediyorsanız, bu sayfanın işletilme şansı bulabilmesi için Application_Error() metodunda ClearError() ile hatayı silmemeniz gerekir. Bu nedenle Application_Error() metodundaki satırlardan birisini sildik. 

Sub
Application_Error(ByVal sender As Object, ByVal e As EventArgs
  Response.Write("Hata meydana geldi")
EndSub 

Bu şartlarda yani Web.config dosyasında CustomErrors ile bir sayfa işaret edilmiş iken Application_Error() metodunu hazırlama amacı olsa olsa meydana gelen hataları loglamak olur. Öncelikli amacımız hata yakalama süreci hakkında bilgi vermek olduğu için yukarı­daki gibi kısıtlı özelliklere sahip bir metot hazırladık. Özetlemek gerekirse, hem Application_Error metodu hem de Web.config dosyasında customErrors tagıyla yapılan ayarlama sitedeki bütün sayfalar üzerinde etkilidir. 

Bu şartlarda yani hem Global.asax dosyasında Application_Error() metodu hazırlanmış hem de Web.config dosyasında özel bir hata sayfası işaret edilmiş iken ilgili sayfanın ErrorPage niteliğiyle bir hata sayfasını işaret etmeniz halinde hem Global.asax dosyasındaki Application_Error() metodu işletilme şansı bulamaz hem de Web.config dosyası aracılığıyla işaret edilen özel hata sayfasına geçilmez. Bu işlemin nasıl yapıldığını anlatmak için sitenin ana sayfasının Page yönergesini aşağıdaki gibi ayarladık. 

<%
@ Page Language="VB" AutoEventWireup="false" 
        ErrorPage ="~/error_page_hata.aspx" 
            CodeFile="Default.aspx.vb" Inherits="_Default" %> 

Bu ayar sayesinde sayfadaki herhangi bir metotta hata meydana gelir ve bu hata try-catch bloklarıyla yakalanmazsa sayfayla ilgili Error olayını temsil eden metot var mı yok mu ona bakılır. Programcı böyle bir metodu hazırlamadıysa istemciye yani ziyaretçiye, sayfanın ErrorPage niteliğine adı aktarılmış sayfa gönderilir. Bu nedenle adını ErrorPage niteliğine aktardığımız sayfayı hazırladık.



Bu konuyu anlatmak üzere hazırladığımız sitedeki ana sayfanın Error olayı için henüz herhangi bir metot hazırlamadığımız için ortama fırlatılan hata, adı ErrorPage niteliğine aktarılan bu sayfa tarafından yakalanır. Şimdi bir adım daha geriye gidip sayfanın Error olayını temsil eden metot bir hazırlayacağız. 

Protected Sub Page_Error(ByVal sender As Object, 
ByVal e As EventArgs) Handles Me.Error 
  Response.Write("Error olayını temsil eden metot işletildi" + "<br>")
End Sub 

Bu şartlarda yani Global.asax dosyasında Application_Error() metodu hazırlanmış, sayfanın ErrorPage niteliğine sitedeki mevcut bir sayfanın adı aktarılmış iken ve sayfanın Error olayını temsil eden bir metot var iken sayfa dahilinde bir hata meydana geldiğinde önce Error olayıyla ilgili metot işletilir. Error olayını temsil eden metotta ClearError() metodu ile son hata silinmediyse veya Redirect() metoduyla başka bir sayfaya geçilmediyse ErrorPage niteliğiyle işaret edilen sayfaya geçilir. Aşağıda verilen ekran görüntüsünü bu şartlarda uzak bir bilgisayarın başında iken sitedeki ana sayfadaki düğmeyi tıklayıp hata oluşmasını sağladıktan sonra aldık.



Tekrar etmek gerekirse, sayfanın Error olayını temsil eden metotta Server nesnesine ClearError() metodunu uygulamış olsaydık adı geçerli sayfanın ErrorPage niteliğine aktarılan bu sayfaya geçilmezdi. 

Şimdiye kadar hata yakalama işlemiyle ilgili olarak yazılanlara bakılırsa ASP.NET sitesin­deki herhangi bir metotta meydana gelen hatayı değişik yerlerde yakalamak mümkündür. Hataların ilk yakalanabilineceği yer hata üretmesi muhtemel ilgili metot dahilinde try-catch bloklarıdır. Try-catch bloklarından sonra sıra söz konusu sayfanın Error olayını temsil eden metodundur. 

Sayfanın Error olayını temsil eden metotta da yakalanmayan hatalar sayfanın Page yönergesinin ErrorPage niteliğine adı aktarılan sayfada oluşan hata yakalanabilir. ErrorPage niteliğinden yararlanıp meydana gelen hata henüz yakalanmadıysa en son olarak Global.asax dosyası dahilinde hazırlanan ve uygulamanın Error olayını temsil eden metotta hata yakalanabilir. Web.config dosyasında CustomErrors tagı ve Global.asax’deki Application_Error() metodu sayesinde ancak sayfa dahilinde yakalanmayan hatalar yakalanıp gereği yapılabilinir.

Ş
imdi web.config dosyası aracılığıyla CustomErrors tagıyla meydana gelen hataların nasıl özel hata sayfalarına yöneltildiğinden söz edeceğiz. Bu amaçla üzerinde çalıştığımız sayfaya bir düğme yerleştirip bu düğmenin Click olayını temsil eden metodu aşağıdaki gibi düzen­ledik. 

Protected
Sub btnGit_Click(ByVal sender As ObjectByVale As EventArgs) Handles btnGit.Click 
   Response.Redirect("Defualt2.aspx")
EndSub 

Bu metot sayesinde sunucudan sitedeki Defualt2.aspx sayfası istenir. Ancak bu sırada sitede böyle sayfa olmadığı için ünlü HTTP 404 nolu hata meydana gelir ve IIS bu hatayla ilgili olarak istemciye aşağıdaki gibi sayfayı gönderir.



Şimdi Web.config dosyası aracılığıyla CustomErrors tagıyla http 404 hatası meydana geldiği zaman istemciye bu klasik sayfa yerine özel bir sayfasının gönderilmesini sağlayacağız. Bu amaçla Web.config dosyasında system.web tagı içinde yer alan customErrors tagını aşağıdaki gibi düzenledik. 

<customErrors mode="RemoteOnly"> 
    <error statusCode="404" redirect="hata404.aspx" />
</customErrors> 

http 404 hatası meydana geldiğinde istemciye hata404.aspx adını verdiğimiz sayfanın gön­derilmesi için sayfanın uzak bir bilgisayardan talep edilmesi veya mode niteliğinin On olarak ayarlanması gerekir. Bu şartlarda yani sitenin ana klasöründeki Web.config dosyasının customErrors tagı bu şekilde düzenlenmiş iken olmayan bir sayfa talep edildiğinde sunucu istemciye “hata404.aspx” sayfasını gönderir. 

Bu sırada kullanıcı yetkisi olmadığı bir sayfayı talep ederse yine IIS standart hata sayfaların­dan birisini gönderir. Bunun önüne geçmek için customErrors tagı içinde ikinci veya üçüncü error tagına yer verip ikinci bir özel hata sayfası hazırlayabilirsiniz. Ancak öngörü­lüp kendisi için özel hata sayfası hazırlamadığınız hatalara özel genel bir hata sayfası hazır­layabilirsiniz. Bu amaçla customErrors tagının defaultRedirect niteliği aşağıdaki gibi düzenlenebilir. 

<customErrors mode="RemoteOnly" defaultRedirect ="genel_hata.aspx"> 
    <error statusCode="403" redirect="yetki_hatasi.aspx" /> 
    <error statusCode="404" redirect="hata404.aspx" />
</customErrors>

ASP.NET | Bu yazıya 1 yorum yapılmış. | 20.04.2010 10:00:37

15. BÖLÜM : OBJECT SINIFI


Bu kitap metni içinde Object sınıfına sık sık gönderme yapılmak zorunda kalındığı için bu sınıf hakkında bilgi sahibi olmak gerek­mekte­dir. Bu bölüm bir bakıma nesneye yönelik programcılık kavramları hakkında bildiklerinizi test etmeye yaramaktadır. Bildiğiniz gibi .NET Framework ile gelen bütün class’lar Object class’ından türetilmek­tedir. Bu Class’ın orijinal yapısı aşağıda verildi.

namespace System
{
 public class Object    
  {
   protected virtual void Finalize()
     {
     }
   public virtual int GetHashCode()
     {
     }
   public virtual bool Equals(System.Object obj)
    {
    }
    public virtual string ToString()
     {
     }
   public static bool Equals(System.Object objA, System.Object objB)
   {
    }
   public static bool ReferenceEquals(System.Object objA, System.Object objB)
    {
    }
   public Type GetType()
    {
     }
   protected System.Object MemberwiseClone()
   {
   }
   public Object()
    {
    }
 }


Bu sınıfın private oldukları için kullanamadığımız FieldSetter(), FeildGetter(), InternalGetType(), GetFieldInfo() ve FastGetExisting() metotlarını bu yapıya dahil etmedik. Bildiğiniz gibi public static metotları herhangi bir hazırlık yapmadan istediği­niz yerde kullanabilirsiniz.

 

İlk olarak Equals() metodunu kullanmayı deneyeceğiz. Equals() metodu object veya başka bir tipteki 2 nesne veya değişkeni parametre olarak alıp birbirine eşit olup olmadı­ğını araştırmaktadır. Bu amaçla ilk olarak Object sınıfının 2 örneğini hazırladık. Object sınıfının yapıcı metodu boş bir nesne hazırlamaktadır. Hemen hatırlatmak gerekirse Object sınıfında Equals() metodunun 2 farklı sürümü bulunmaktadır. 

private void Form1_Click(object sender, EventArgs e)
 {
   Object Nesne1, Nesne2;
   Nesne1 = new Object();
   Nesne2 = new Object();
 }

Bu şekilde Object sınıfının 2 örneği hazırlanıp bu nesneler Equals() metodu ile karşılaştı­rılırsa nesneler birbirine eşit olmazlar. Bu nedenle aşağıda verilen kodda Equals() metodu “Nesne1” ile Nesne2’in birbirine eşit olmadıklarına hükmedir.

private void Form1_Click(object sender, EventArgs e)
 {
   Object Nesne1, Nesne2;
   Nesne1 = new Object();
   Nesne2 = new Object();
   bool sonuc;
   sonuc = Object.Equals(Nesne1, Nesne2);
   if (sonuc == true)
     MessageBox.Show("Bu nesneler birbirine eşit"); 
 }

Object tipindeki bu 2 nesneyi Equals() metodu ile birbiri ile karşılaştırmadan önce aşağıda yapıldığı gibi aynı değeri aktarmış olsaydık Equals() metodu bu kez geriye true gönderirdi. Bu kodda Object tipindeki bu 2 değişkene sayısal bilgi yerine null aktarmış olsaydık bile Equals metodu geriye true gönderirdi. 

private void Form1_Click(object sender, EventArgs e)
 {
   Object Nesne1 = new Object();
   Object Nesne2 = new Object();
   Nesne1 = 1234;
   Nesne2 = 1234;
   bool sonuc = Object.Equals(Nesne1, Nesne2);
   if (sonuc == true)
      MessageBox.Show("Bu nesneler birbirine eşit"); 
   else
      MessageBox.Show("Bu nesneler birbirine eşit değil");
  }     

Bu sonuca bakıp Equals() metodunun karşılaştırmayı yalnızca referans bazında yapmadı­ğına karar verebilirsiniz. Şimdi Object sınıfının Equals() adındaki statik metoduna biraz yakından bakalım. Bu metot kendisine parametre olarak verilen 2 nesne veya değişkenin(bu değiş­kenlerin object tipinde olması şart değildir) referanslarının aynı olup olmadığına bakar. Her 2 parametre veya nesne de aynı referansı işaret ediyorsa geriye true göndermektedir. Equals() metoduna parametre olarak verilen nesneler veya değiş­kenler aynı adreste saklanmıyorsa bu kez içeriklerinin aynı olup olmadığına bakılır.

 

Yukarıda Equals() metoduna direk Object tipindeki bilgileri parametre olarak verdik. Bu metot istenen tipteki bilgi veya nesneleri karşılaştırabilir. Aşağıda verilen örmekte ise Int32 tipindeki 2 değişkeni veya System.Int32 sınıfından oluşturulmuş 2 nesneyi Equals() metoduna parametre olarak verip birbiri ile karşılaştırdık. 

private void Form1_Click(object sender, EventArgs e)
 {
   System.Int32 Sayi1 = new System.Int32();
   System.Int32 Sayi2 = new System.Int32();
   Sayi1 = 100;
   Sayi2 = 100;
   bool sonuc = Object.Equals(Sayi1, Sayi2);
   if (sonuc == true)
      MessageBox.Show("Bu nesneler birbirine eşit"); 
 }

Equals() metodu System.Int32 sınıfından türetilmiş nesneleri karşılaştırırken onların refe­ransları yerine içeriklerine baktığı için burada yapılan karşılaştırma doğru değerini verir. Burada Int32 yapısının örneklerini new anahtar kelimesi ile aldıktan sonra bilgi aktardık. Eğer bu koddaki değişkenlere bilgi aktaran satırları kaldırırsanız değişen bir şey olmaz ve değişkenlerin birbirine eşit olduğuna hükmedilir. 

Çünkü değer tipindeki değişkenler new anahtar kelimesi ile hazırlandığında varsayılan başlangıç değer otomatik olarak aktarılıyor. Değişkenler adlı bölümden hatırlayabilece­ğiniz gibi double ve int gibi değişkenler new anahtar kelimesi tanımlanıp değer aktarıl­madan kullanılmak istenirse hata meydana gelmektedir. Konunun iyice anlaşılmasını sağlamak için aşağıda verilen örnekte TextBox sınıfından türetilmiş 2 nesneyi karşılaş­tırdık.

 
private void Form1_Click(object sender, EventArgs e)
  {
     System.Windows.Forms.TextBox Text1;
     System.Windows.Forms.TextBox Text2;
     Text1 = new TextBox();
     Text2 = new TextBox();
     bool sonuc = Object.Equals(Text1, Text2);
     if (sonuc == true)
        MessageBox.Show("Bu nesneler birbirine eşit"); 
     else
       MessageBox.Show("Bu nesneler birbirine eşit değil");
  }

Bu örnekte TextBox’lara bilgi aktarmadan karşılaştırdık. Bu sırada her 2 TextBox’ın özellikleri aynı olsa veya aynı bilgileri içerseler bile bu karşılaştırmada nesnelerin birbirine eşit olmadığına hükmedilir. Çünkü Equals() metodu referans tipleri karşılaştı­rırken nesnele­rin referanslarına yani bellekteki adreslerine bakmaktadır. 

Bu şartlarda TextBox sınıfının Text1 ve Text2 adını verilen her iki örneği her şeyi ile aynı özelliklere sahip olsalar bile referansları farklı olduğu için Equals() metodu bu 2 nesnenin eşit olmadığını rapor eder. Ancak aşağıda yapıldığı gibi bu TextBox nesneleri birbirine eşitlenip bellekte aynı adresi kullanmaları sağlandığında Equals() metodu geriye true gönde­rir ve parametre olarak aldığı nesnelerin birbirine eşit olduğuna hükmeder.

TextBox Text1 = new TextBox();
TextBox Text2 = new TextBox();
Text1.Text = "Fenerbahçe";
Text2 = Text1; 
bool sonuc = Object.Equals(Text1, Text2);
if (sonuc == true)
  MessageBox.Show("Bu nesneler birbirine eşit"); 
 

Tekrar etmek gerekirse Equals() metodunu çağıran ve parametre olarak verilen nesne Refe­rans Type olmasaydı bu kez içeriklerinin aynı olup olmadığına bakılırdı. Bu nedenle aşağıda verilen kodda Equals() metodu geriye true gönderir. Çünkü int tipindeki 2 değişken veya nesneye aynı bilgiyi aktardık.

private void Form1_Click(object sender, EventArgs e)
 {
   int Sayi1 = 100;
   int Sayi2 = 100;
   bool sonuc = Sayi1.Equals(Sayi2);
   MessageBox.Show(sonuc.ToString());    
 }

Bu sayfaya kadar Object sınıfının Equals() metodunu anlatırken sürekli .NET Framework ile gelen hazır tip ve sınıfları kullandık. Equals() metodunun kendi hazırladı­ğınız tiplere davranışı farklı olduğu için aşağıdaki gibi bir Class hazırladık.

public
class Takim
 {
   public string takim_adi;
   public string baskan;
   public Takim(string par1, string par2)
    {
      takim_adi = par1;
      baskan = par2; 
    }
  }

Bu şekilde 2 alana ve bir yapıcı metoda sahip sınıf hazırladıktan sonra aşağıdaki gibi kod yazıp bu sınıfın 2 örneğini alıp Equals() metodu ile karşılaştırdık. Beklenilenin aksine Object sınıfının Equals() metodu bu 2 nesnenin birbirinden farklı olduğunu rapor eder.

private
void Form1_Click(object sender, EventArgs e)
 {
   Takim T1 = new Takim("Fenerbahçe", "Aziz Yıldırım");
   Takim T2 = new Takim("Fenerbahçe", "Aziz Yıldırım");
   if (Object.Equals(T1, T2))
      MessageBox.Show("Bu nesneler birbiri ile aynı");
   else
      MessageBox.Show("Bu nesneler birbirinden farklı");
  }

Yukarıdaki sayfalarda .NET Framework ile değişik tipteki nesneleri karşılaştırırken nesneler aynı içeriğe sahip olduklarında Equals() metodu bu nesnelerin birbirine eşit olduğunu söylüyordu. T1 ve T2 adını verdiğimiz bu 2 nesnenin birbirinden farklı olarak görülmesinin nedeni Equals metodunu “Takim” adını verdiğimiz sınıfımız için yeni­den yazmamış olmamızdır. “Takim” adını verdiğimiz Class’ın Object sınıfından gelen Equals() metodunu override edip nasıl yeniden hazırladığımızı aşağıda görebilirsiniz. Bu hazırlıktan sonra Equals() metodu Compare işlemi yapıp yalnızca kendisine parametre olarak verilen nesnedeki alanla­rın içeriklerine göre karar verir.

public
class Takim
  {
    public string takim_adi;
    public string baskan;
    public Takim(string par1, string par2)
     {
       takim_adi = par1;
       baskan = par2; 
     }
    public override bool Equals(object obj)
      {
       Takim T = (Takim)obj;
       if ((T.takim_adi == this.takim_adi) && (T.baskan == this.baskan))
         return true;
       else
        return false;
     }
  }

Bu hazırlıktan sonra yukarıda verilen “Object.Equals(T1, T2)” satırında Equals() metodu geriye true gönderir. Şimdi benzer ayarlamayı TextBox sınıfının mirasçısı kendi hazırlayaca­ğımız bir sınıfta yapacağız. Yapacağımız ayarlama sonucu birbirleri ile karşılaştı­rılan TextBox nesnelerinin Text özellikleri aynı ise bir birine eşit oldukları kabul edilecektir
.

public
class Edit : TextBox
 {
   public override bool Equals(object obj)
    {
      Edit Edit1 = (Edit)obj;
      if (Edit1.Text == this.Text)
         return true;
       else
         return false;
     }
  }

Bu kodu yazmamdaki amaç TextBox ve başka sınıfların Object sınıfından miras aldıkları metotların nasıl override edildiklerine dikkatinizi çekmektedir. Bu metodun nasıl kulla­nıldığını göstermek için aşağıda verdiğimiz kodu yazdık.

private void button1_Click(object sender, EventArgs e)
 {
   Edit Text1 = new Edit();
   Edit Text2 = new Edit();
   bool sonuc = Object.Equals(Text1, Text2);
   if (sonuc == true)
      MessageBox.Show("Bu nesneler birbirine eşit");
   else
     MessageBox.Show("Bu nesneler birbirine eşit değil");
 }

Şimdiye kadar Object sınıfının Equals() metodunu kullanırken karşılaştırmak istediğimiz 2 nesneyi bu metoda parametre olarak verdik. Şimdiye kadar kullandığımız Equals() metodu Object sınıfının static bir metodu olduğu için direk kullanıyorduk. 

Ancak isterseniz Object sınıfının static olmayan ve nesne üzerinden çağrılan Equals() metodunu aşağıdaki gibi kullanabilirsiniz. Burada Equals() metodunu çağıran nesne ile metoda parametre olarak verilen nesne ile aynı referansa veya içeriğe sahipse geriye true gönderilir. Bu örnekte bu nesnelerin referansları farklı olduğu için geriye false gönderilir.

TextBox Text1 = new TextBox();
TextBox Text2 = new TextBox();
bool sonuc = Text1.Equals(Text2);
MessageBox.Show(sonuc.ToString());

ReferanceEquals Metodu
 

Bu bölümün ilk sayfasında verilen Object sınıfının yapısını incelediğinizde Equals() meto­dundan başka ReferanceEquals() adında static bir metodun olduğunu görürsünüz. Bu metot kendisine parametre olarak verilen 2 nesne veya değişkenin referanslarının yani bellekteki adreslerinin aynı olup olmadığını araştırmaktadır. 

Yukarıda hakkında bilgi verilen Equals() metodu konu Değer Tipleri olduğunda değiş­ken veya nesnenin içeriğiyle ilgileniyordu. ReferenceEquals() metodu ise kendisine parametre olarak verilen tiplerin referanslarının aynı olup olmadığını araştırmaktadır. Bu nedenle ReferenceEquals() metodu aşağıdaki gibi kullanıldığında geriye false gönderir. 

private void Form1_Click(object sender, EventArgs e) 
 
   int sayi1 = 100; 
   int sayi2 = 100; 
   if (Object.ReferenceEquals(sayi1, sayi2)) 
     MessageBox.Show("Bu değişkenlerin referansları aynı"); 
   else 
     MessageBox.Show("Bu değişkenlerin referansları farklıdır"); 
 

Yine yukarıdaki sayfalarda Equals() metodu hakkında yazılanlardan bildiğiniz gibi Equals() metoduna aynı içeriğe sahip 2 string değişken parametre olarak verildiğinde geriye true gönderilmektedir. Ne var ki konu ReferenceEquals() metodu olduğunda bekle­nilenden farklı bir sonuç alınmaktadır. Yani aynı içeriğe sahip 2 string değişkeni aşağıda yapıldığı gibi ReferenceEquals() metoduna parametre olarak verirseniz bu 2 değişkenin referanslarının birbirine eşit olduğu rapor edilir. 

 
string Takim1 ="Fener"
 string Takim2 = "Fener"
 if (Object.ReferenceEquals(Takim1, Takim2)) 
   MessageBox.Show("Bu değişkenler birbirine eşit"); 
 else 
   MessageBox.Show("Bu değişkenler birbirine eşit değil"); 

Bu sonucun açıklaması şu olabilir: .NET Framework ile gelen string sınıfında ReferenceEquals metodu override edilip aynı içeriğe sahip string değişkenlerin birbirine eşit olması sağlanmaktadır. Benzer durum object tipindeki değişkenler için de geçerlidir. Bu örnekte değişkenlerin tipi object yapılırsa ReferenceEquals() metodu geriye yine true gönderir. 

    
object Takim1 ="Fener"; 
     object Takim2 = "Fener"; 
     if (Object.ReferenceEquals(Takim1, Takim2)) 
       MessageBox.Show("Bu değişkenler birbirine eşit"); 
     else 
       MessageBox.Show("Bu değişkenler birbirine eşit değil"); 

Tekrar etmek gerekirse; string ve object değişkenler hariç ReferenceEquals() metodu kendisine parametre olarak verilen değişken veya nesneleri karşılaştırırken referansları­nın aynı olup olmadığına bakıyor. Bu nedenle aşağıdaki gibi 2 TextBox nesnesi hazırlayıp bunları ReferenceEquals() metodu sayesinde birbirleri ile karşılaştırırsanız nesnelerin birbirine eşit olmadıkları rapor edilir. 

private
void Form1_Click(object sender, EventArgs e) 
 
   TextBox Nesne1 = new TextBox(); 
   TextBox Nesne2 = new TextBox(); 
   if (Object.Equals(Nesne1, Nesne2)) 
      MessageBox.Show("Bu nesneler birbirine eşit"); 
   else 
      MessageBox.Show("Bu nesneler birbirine eşit değil"); 
 
}
  

ReferenceEquals
() metodu benzer davranışı kendi hazırladığınız sınıfların örneklerini karşılaştırırken de gösterir. Örneğin aşağıdaki gibi kısıtlı özelliklere sahip bir sınıf hazırla­yıp bu sınıftan yola çıkılarak alanları aynı olan 2 kopyası hazırlanıp bu kopyalar birbiri ile karşılaştırıldığı zaman birbirine eşit olmadıklarına karar verilir. 

private void Form1_Click(object sender, EventArgs e) 
 
   Takim Takim1 = new Takim("Fenerbahçe", "Aziz Yıldırım"); 
   Takim Takim2 = new Takim("Fenerbahçe", "Aziz Yıldırım"); 
   if (Object.ReferenceEquals(Takim1, Takim2)) 
      MessageBox.Show("Bu nesneler eşit"); 
   else 
      MessageBox.Show("Bu nesneler eşit değil"); 
 }
public class Takim 
 
   public string takim_adi; 
   public string baskan; 
   public Takim(string par1, string par2) 
   
      takim_adi = par1; 
      baskan = par2; 
  
 }

ToString() ve GetType() Metotları 

Object sınıfı­nın en çok kullanılan metodu ToString() metodudur. Bütün sınıflar Object sınıfından türetildiği için ToString() metodu bütün yapılar ve Class’lar tarafından destek­lenmektedir. Örneğin aşağıda verilen satırda kullanılan ToString() metodu, Int32 yapısı­nın(struct) Object sınıfından miras aldığı metottur. 

Int32 Sayi = new Int32();  // veya int Sayi;
Sayi = 1234;
textBox1.Text = Sayi.ToString(); 

Her ne kadar Int32 yapısı ToString() metodunu Object sınıfından miras almış olsa bile bu metot override edilip yeniden yazılmıştır. Herhangi bir müdahalede bulunmadığınız sürece ToString() metodun orijinal hali kendi sınıfınızın tam adını verir. Bu konuda bilgi vermek için yukarıda verilen “Takim” sınıfını aşağıdaki gibi düzenledik. 

public class Takim 
 
   public string takim_adi; 
   public int kurulus; 
   public Takim(string par1, int par2) 
   
       takim_adi = par1; 
       kurulus = par2; 
   
 } 

Daha sonra bu sınıfın örneğini alıp nesneye ToString() metodunu aşağıdaki gibi uygular­sanız tahmin edeceğiniz gibi formun başlığına önce Class’ın dahil olduğu namespace’in, ardından Class’ın adı yazılır. 

private void Form1_Click(object sender, EventArgs e) 
 
   Takim Takim1 = new Takim("Fenerbahçe", 1907); 
   this.Text = Takim1.ToString();  
 } 

Bu kod işletildiğinde aşağıdaki gibi bir sonuç alınır. Buradaki “Proje1” üzerinde çalıştığımız uygulama dahilinde hazırlanan namespace’in adıdır. Başka bir deyişle “Takim” tipindeki “Takim1” nesnesine ToString() metodu uygulandığında sınıfın adı elde edilmektedir.



Hal bu ki Int32 yapısının örneğine(değişken) ToString() metodu uygulandığı zaman değişkenin içerinin string halini elde ediyorduk. Kendi hazırladığınız sınıfta ToString() metodundan farklı işlevler bekliyorsanız Object sınıfından miras alınan bu metodu yeniden yazmalısınız. ToString() metodunu aşağıdaki gibi kullansaydık formun başlığına 1907 yazılırdı. Çünkü “Takim” sınıfının “kurulus” alanı Int32 tipinde olduğu için burada kullanı­lan ToString() metodu Int32 yapısına ait metottur. 

private void Form1_Click(object sender, EventArgs e) 
 
   Takim Takim1 = new Takim("Fenerbahçe", 1907); 
   this.Text = Takim1.kurulus.ToString();  
 } 

Şimdi Object sınıfından gelen ToString() metodunu “Takim” adını verdiğimiz kendi sınıfımızda override edip yani yeniden yazıp farklı bir işleve sahip olmasını sağlayacağız. Ancak ToString() metodunu yeniden yazmadan önce yukarıda “Takim1” nesnesine uyguladığımız ToString() metodunun neden Namespace adına sınıfımıza verdiğimiz adı ekleyip geriye gönderdiğini açıklayacağız. “Takim” sınıfının sınırları içinde public override yazıp boşluk tuşuna basınca Visual Studio aşağıdaki gibi 3 metot listeledi.




Çünkü bu sırada “Takim” adını verdiğimiz sınıf dahilinde override edilebilinecek 3 metot yani 3 virtual metot vardı. Bu 3 metodun da Object sınıfından miras yolu alınan metotlar olduğunu biliyor­sunuz. Bu bölümün ilk sayfasına bakarsanız toplam 3 virtual metodun olduğunu görürsü­nüz. Listelenen bu 3 metottan ToString’i seçmeniz halinde Visual Studio otomatik olarak ToString() metodunun Object sınıfından alınan orijinal halini hazırlar. 

public class Takim 
 
   public string takim_adi; 
   public int kurulus; 
   public Takim(string par1, int par2) 
   
      takim_adi = par1; 
      kurulus = par2; 
   
   public override string ToString() 
   
      return base.ToString(); 
   
  } 

Gördüğünüz gibi ToString() metodunun orijinal halinde base yani taban sınıfın adı geriye gönderilmektedir. Şimdi Visual Studio tarafından hazırlanan bu ToString() meto­dunda öyle bir değişiklik yapacağız ki geriye “Takim” sınıfından yola çıkılarak hazırlanan nesnenin “takim_adi” ve “kurulus” alanların birleştirilmiş içeriğini geriye gönderecektir. 

public class Takim 
 
   public string takim_adi; 
   public int kurulus; 
   public Takim(string par1, int par2) 
   
      takim_adi = par1; 
      kurulus = par2; 
   
   public override string ToString() 
   
     return "Takim_adi : " + this.takim_adi + 
      " " + "\n" + "Kuruluş Yılı : " + this.kurulus.ToString(); 
  
 

Bu şekilde düzenlenen ToString() metodunun pratik değeri yoktur. Çünkü geriye gönde­rilecek bilginin formatını seçerken MessageBox’ın Show() metodunu gözettik. Yukarıda Form1_Click() metoduna yazdığımız kod işletilirse aşağıdaki gibi bir sonuç alınır.



Şimdi yukarıda Object sınıfından türettiğimiz nesnenin türünü Object sınıfının GetType() metodu ile öğreneceğiz. Bütün sınıflar Object sınıfının mirasçısı olduğu için bütün sınıflar GetType() metoduna sahiptir. 

private void Form1_Click(object sender, EventArgs e) 
 
   Object nesne = new Object(); 
   object Tip = nesne.GetType();  
   textBox1.Text = Tip.ToString();  
 

Bu kodda object tipinde bir değişken tanımlayıp GetType() metodunun geriye gönder­diği bilgiyi bu değişkene aktardık. Bu kod işletildiğinde TextBox’a System.Object yazılır. GetType() metodunu aşağıdaki gibi kullanmış olsaydık TextBox’a System.Byte yazılırdı. 

byte Sayi = 128;
object Tip = Sayi.GetType();
textBox1.Text = Tip.ToString(); 

Nenenin tipini veya türetildiği sınıfı öğrenmek istiyorsanız typeof operatöründen yararla­nabilirsiniz. Bu operatör parametre olarak tipi öğrenilmek istenen nesne veya tipi alıp geriye System.Type tipinde bilgi göndermektedir. Bu sınıfın ve typeof() operatörünün nasıl kulla­nıldığını anlatmak için aşağıda verilen örneği hazırladık. 

private void Form1_Click(object sender, EventArgs e) 
 
   matematik nesne = new matematik(); 
   if (nesne.GetType() == typeof(matematik)) 
    MessageBox.Show("Bu nesne matematik sınıfından türetilmiş"); 
 

GetHashCode() Metodu 

Object sınıfının yukarıdaki sayfalarda verilen yapısını incelerseniz geriye int tipinde bilgi gönderen ve herhangi bir parametreye sahip olmayan GetHashCode adında bir metoda sahip olduğunu görürsünüz. Kısaca söylemek gerekirse, bu metot nesnelerin Hash kodu öğrenil­mek istendiği zaman kullanılmaktadır.
.NET uyumlu uygulamalarda hazırlanan her nesne veya tanımlanan her değişken HashCode adı verilen sayısal bir koda sahiptir. Bu kod öğrenilerek söz konusu nesneye kolayca ulaşmak mümkün olmaktadır. Bu metodun nasıl kullanıldığını anlat­mak için forma 2 TextBox yerleştirip aşağıda verilen kodu hazırladık. 

private void Form1_Click(object sender, EventArgs e) 
 
    Button Dugme1 = new Button(); 
    Button Dugme2 = new Button(); 
    int kod1 = Dugme1.GetHashCode(); 
    int kod2 = Dugme2.GetHashCode(); 
    textBox1.Text = kod1.ToString(); 
    textBox2.Text = kod2.ToString(); 
 

Bu kod işletildiğinde aşağıdaki gibi bir sonuç alınır. Aynı oturumda bile aynı kodu tekrar işlettiğinizde farklı hash kodları elde edilir. Çünkü kod tekrar işletildi­ğinde nesne yeniden hazırlanır ve belleğin farklı bir yerinde kendisine yer bulup farklı bir Hash koduna sahip olur.



Konuya başka bir açıdan bakabilmek için Windows Forms uygulamasında “Form1” sınıfının her yerinde yaşayacak TextBox tipinde değişken tanımladık. Ardından bu sınıfın Form1_Load() metodunda TextBox nesnesini hazırladık.



Proje çalıştırıldığında bu satırlar sayesinde TextBox nesnesi hazırlanıp HashCode’u elde edilir. Programın ileri aşamalarında “Form1” sınıfının her yerinde yaşayan bu nesneye gerek duyduğunuzda HashCode’dan yararlanabilirsiniz. 

   
private void Form1_Click(object sender, EventArgs e) 
    
      if (kod == this.Text1.GetHashCode()) 
       MessageBox.Show("ilgilendiğim nesne bu");  
   

Nesne ve değişkenlerin HashCode’ları ilgili olarak anlatacaklarımız bunlarla sınırlı değil­dir. HashCode() metodunun diğer özelliklerini ortaya çıkarmak için aşağıdaki gibi aynı içeriğe sahip 2 string değişken tanımlayıp hash kodlarını öğrendik. 

private void Form1_Click(object sender, EventArgs e) 
 
   string str1 = "Fenerbahçe"; 
   string str2 = "Fenerbahçe"; 
   int kod1 = str1.GetHashCode(); 
   int kod2 = str2.GetHashCode(); 
   textBox1.Text = kod1.ToString(); 
   textBox2.Text = kod2.ToString();
} 

B
urada tanımladığımız string değişkenler aynı içeriğe sahip oldukları için aynı Hash koduna sahip olurlar. Bu değişkenlerin içerikleri farklı olsaydı hash kodları da farklı olurdu. Şimdi ise yukarıdaki sayfalarda hazırlayıp “Takim” adını verdiğimiz sınıfın aynı içeriğe sahip 2 örneğini alıp Hash kodlarını öğreneceğiz. 

private void Form1_Click(object sender, EventArgs e) 
 
   Takim Takim1 = new Takim("Fenerbahçe", "Aziz Yıldırım"); 
   Takim Takim2 = new Takim("Fenerbahçe", "Aziz Yıldırım"); 
   int kod1 = Takim1.GetHashCode(); 
   int kod2 = Takim2.GetHashCode(); 
   textBox1.Text = kod1.ToString(); 
   textBox2.Text = kod2.ToString(); 
} 

Bu kod işletildiğinde bu iki nesnenin farklı hash kodlarına sahip oldukları görülür. Eğer “Takim” sınıfını aşağıdaki gibi yapı(struct) olarak düzenlemiş olsaydık bu kez farklı bir sonuç alınırdı ve her iki nesne de aynı hash koduna sahip olurdu. 

public struct Takim 
 
  public string takim_adi; 
  public string baskan; 
  public Takim(string par1, string par2) 
  
     takim_adi = par1; 
     baskan = par2; 
   
 } 

Struct’lar value tipler oldukları için aynı içeriğe sahip nesnelere aynı hash kodu verilmektedir. Şimdi “Takim” adını verdiğimiz bu yapının örneğini alırken 2. alanın farklı bir içeriğe sahip olmasını sağlayacağız. 

Takim Takim1 = new Takim("Fenerbahçe", "Aziz Yıldırım");
Takim Takim2 = new Takim("Fenerbahçe", "Ali Şen");
int kod1 = Takim1.GetHashCode();
int kod2 = Takim2.GetHashCode();
textBox1.Text = kod1.ToString();
textBox2.Text = kod2.ToString(); 

Bu kod işletildiğinde beklenilenin aksine her iki TextBox’a aynı hash kodu yazılır. Çünkü bu tip nesnelere hash kodu verilirken yalnızca ilk alanın içeriğine bakılmaktadır. Bu örnekte Takim1 ve Takim2 adlarını verdiğimiz 2 nesnenin ilk alanları(takim_adi) aynı içeriğe sahip olduğu için her iki nesne de aynı hash koduna sahip olur. 

C# 3.0 Kitabı | Bu yazıya 11 yorum yapılmış. | 12.01.2010 04:23:18

14. BÖLÜM : STRUCT ve ENUM TİPİ HAZIRLAMAK(*)


Struct yani yapılar Class’lara çok benzemektedir. Aralarındaki en önemli fark, bellekte tutuldukları yerdir. Class’lar belleğin Heap kısmında tutulan referans tür iken; yapılar değer türleri olup stack’te tutulmaktadır. Yapılar, tıpkı sınıflar gibi metotlar, yapıcı metotlar, sabitler, özellikler, alanlar içerebilirler. Struct’lar başka Struct’ın veya Class’ın mirasçısı olamaz. Ancak struct’lara interface’ler uygulanabilmektedir. Yapılar kalıtımı desteklemedikleri için abstract veya virtual olarak işaret edilemezler. Kısaca anlatmak gerekirse struct’ları bazı özellikleri kırpılmış Class’lar olarak ifade etmek mümkündür. 

Ne zaman class, ne zaman yapı hazırlanması gerektiği konusu tartışmaya açık olmakla birlikte struct’ın örneği bellekte fazla yer kaplamayacaksa struct’ı tercih edebilirsiniz. Aşağıda verilen uygulamada “adres” adında ve 3 alana sahip bir yapı tanımladık.



Bu struct’ı incelerseniz Class ile sınırlanan bloğun içine yazıldığını görebilirsiniz. Kodu okunur yapmak için struct; class tarafından sınırlanan bloktan çıkarılabilir. Yapıda yer alan alanlar istenen tipte olabilir. Bu şekilde tanımlanan yapıyı kullanabilmek için yapıdan yola çıkıp nesne hazırlamak gerekir. Bu işlemin nasıl yapıldığını aşağıda görebilirsiniz. 

namespace yapilar

  class sinif_1 
  
    public static void Main() 
    
       oyuncular nesne; 
       nesne = new oyuncular(); 
     
  
 struct oyuncular 
  
    public string ad; 
    public double boy; 
    public string takim; 
  }


Bu nesneyi Main() metodu içinde hazırladığımız için ancak bu metodun içinde kullanılabilir. Bu şekilde tanımlanan yapının elemanlarına istenildiği gibi bilgi aktarılabilinir. Tanımlanan yapının 3 elemanına aşağıda bilgi aktarılmaktadır.



Konunun iyice anlaşılmasını sağlamak için Visual Studio ile hazırladığımız projede bir yapı hazırlayıp kullanacağız. Bu kez hazırlayacağımız yapıyı başka class veya formlarda kullanabilmek için public yapacağız. Bildiğiniz gibi aynı namespace içinde istenilen sayıda Class olabilmektedir. Hazırladığınız Struct’ları mevcut kod dosyası içinde Class’lar ile bir arada tutabileceğiniz gibi başka bir CS dosyasına alabilirsiniz.



“oyuncular” adını verdiğimiz struct’ı bu şekilde tanımladıktan sonra bu yapıdan yola çıkarak “oyuncu” adında bir değişken tanımladık yani nesne hazırladık. Hem yapının elemanları public olduğundan hem de yapıdan türettiğimiz nesneyi static yaptığımız için diğer formlarda yapının elemanlarına nesne hazırlanmadan erişim sağlanabilir.



Struct tipindeki değişkenin elemanlarına bu şekilde bilgi aktardıktan sonra projedeki 2. forma geçip aşağıda verilen satırları yazdık. “nesne” adını vermiş olduğumuz değişken public olduğu için her form ve sınıfta kullanılabilir.



Struct’lar İçin Yapıcı Metot Hazırlamak 

Y
ukarıda belirtildiği gibi Struct’lar Class’lar gibi constructor(yapıcı) metotlara sahip olabilirler. Şimdi yukarıda hazırladığımız yapıyı biraz değiştirip constructor metoda sahip olmasını sağlayacağız. Bildiğiniz gibi constructor metotlar, sınıfların veya yapıların örnekleri alındığı zaman otomatik olarak işletilen ve struct ile aynı ada sahip metotlardır. Struct’ların yapıcı metotları Class’ların aksine parametreli olmak zorundadır. 

public struct oyuncular 
 
   public string ad; 
   public double boy; 
   public string takim; 
   public oyuncular(string Par1, double Par2, string Par3)
   
      this.ad = Par1; 
      this.boy = Par2; 
      this.takim = Par3; 
   
  

Bu şekilde düzenlenen yapının örneği hazırlandığında struct ile aynı ada sahip bu metot işletilir. Nesnenin örneği hazırlanırken parametreler kullanılmayabilir. Bu nedenle aşağıda verilen metot sorunsuz çalışır. 

private void Form1_Click(object sender, EventArgs e) 
 
   oyuncular nesne = new oyuncular(); 
   MessageBox.Show(nesne.ad);   
 

Struct için yapıcı metot hazırlanmamışsa alanlara ilk değer verme zorunluluğu vardır. new anahtar kelimesi kullanılmadan yapıların örneği alınabilir. Bu durumda yapının alanlarına ilk değer aktarılması gerekir. Yukarıda örnek olması için hazırladığımız struct 3 alana sahip olduğu ve dolayısıyla yapıcı metot 3 parametreye sahip olduğundan yapının örneği hazırlanırken parametre kullanılacaksa hepsi kullanılmalıdır. 

private void Form1_Click(object sender, EventArgs e)

  oyuncular nesne; 
  nesne = new oyuncular("Kerem Gönlüm", 2.10, "Efes Pilsen"); 
  textBox1.Text = nesne.ad; 
  textBox2.Text = Convert.ToString(nesne.boy); 
  textBox3.Text = nesne.takim;


Struct’ların yapıcı metotları Class’larınki gibi aşırı yüklenebilir veya değişik sayıda parametreye sahip versiyonları olabilir. Bu imkanı göstermek için yukarıda verilen yapıcı metodun 2 parametreli versiyonunu hazırlayıp kullandık. 

public struct oyuncular 
 
   public string ad; 
   public double boy; 
   public string takim; 
   public oyuncular(string Par1, double Par2, string Par3) 
   
      this.ad = Par1; 
      this.boy = Par2; 
      this.takim = Par3; 
   
   public oyuncular(string Par1, double Par2) 
    
        this.ad = Par1; 
        this.boy = Par2; 
        this.takim = ""; 
      
  

2 parametreli yapıcı metotta Struct’ın “takım” adını verdiğimiz 3. elemanına varsayılan değeri aktardık. String tipindeki 3. elemana bu şekilde boşluk aktarmasaydık hata meydana gelirdi. Diğer yandan Struct’lar için yok edici metot hazırlanamaz. 
Class’lar hakkında bilgi verilen bölümden hatırlayacağınız gibi .NET 2.0 ile birlikte Class’ların birden fazla parçaya bölünmesi, bir Class’a ait kodların birden fazla dosyada tutulmasına izin verilmeye başlandı. Birden fazla parçaya bölünmek istenen Class’ların partial olarak işaret edilmesi gerektiğini biliyorsunuz. Benzer şekilde Struct’ları partial olarak hazırlayıp birden fazla parçaya bölebilir veya kod dosyasına ayırabilirsiniz. Bu işlemin nasıl yapıldığını hatırlatmak için yukarıda verilen Struct’ı 2 parçaya böldük.



Yukarıda struct’ı önce Class’ın içinde hazırladık. Daha sonra Struct’ı Class tarafından sınırlanan bloğun dışına almıştık. Class’ların içinde Struct hazırlandığı gibi Struct’ların içinde Class hazırlanabilir. Bu nedenle aşağıda verilen kod hataya neden olmaz.

public partial struct oyuncular
{
  public string ad;
  public double boy;
  public string takim;
  public oyuncular(string Par1, double Par2, string Par3)
   {
    this.ad = Par1;
    this.boy = Par2;
   this.takim = Par3;
  }
 class sinif
  {
  }
}


Şimdi pratik değeri olmasa bile struct’lar hakkında anlatılanların kavranmasına katkı yapacak bir örnek hazırlayacağız. Bu amaçla projenin formunu aşağıdaki gibi düzenledik. Oyuncuya ait bilgiler TextBox’lara girilip ‘Ekle’ düğmesi tıklanacak.



“Ekle” düğmesi tıklandığında struct’ın örneği hazırlanıp TextBox’ların içerikleri struct’ın elemanlarına aktarılacaktır. Devamında ise oyuncular tipindeki dizi değişkene aktarılacak. En son olarak dizi değişkende saklanan bilgiler diğer formdaki ListBox’lara aktarılacaktır. 

“Dizi” değişkenindeki bilgiler diğer formdaki ListBox’lara aktarılmak istediğinde ise “Aktar” düğmesi tıklanacak. “Ekle” düğmesi için hazırladığımız kod aşağıdadır. 

oyuncular[] Dizi, Dizi_temp;
private void ekle_Click(object sender, EventArgs e) 
 
    oyuncular nesne; 
    string str_ad = textBox1.Text; 
    double boyu = Convert.ToDouble(textBox2.Text); 
    string str_takim = textBox3.Text; 
    nesne.ad = str_ad; 
    nesne.boy = boyu; 
    nesne.takim = str_takim;  
    textBox1.Text = ""; 
    textBox2.Text = ""; 
    textBox3.Text = ""; 
    if (Dizi == null) 
    
       Dizi = new oyuncular[1]; 
       Dizi[0] = (oyuncular)nesne; 
    
    else
    
      int sayi = Dizi.Length; 
      Dizi_temp = new oyuncular[sayi]; 
      for (int i = 0; i < sayi; i++) 
        
          Dizi_temp[i] = Dizi[i]; 
        
       Dizi = new oyuncular[sayi + 1]; 
       for (int i = 0; i < sayi; i++) 
         
           Dizi[i] = Dizi_temp[i]; 
         
       Dizi[sayi] = (oyuncular)nesne; 
     
  

Son derece basit olan bu kodda önce “Ekle” düğmesinin Click olayını temsil eden metodun üst kısmında “Dizi” ve “Temp_dizi” adında ve oyuncular tipinde(“oyuncular” hazırlamış olduğumuz yapıdır) 2 dizi değişken tanımladık. Oyuncu ekleme işleminden sonra her seferinde bu dizi değişken yeniden boyutlanacaktır. TextBox’lara girilen bilgileri “oyuncular” adını verdiğimiz struct’ın örneğinin alanlarına aktarıp  TextBox’ların içeriklerini temizledik. 

Devamında dizi değişkenin null olup olmadığını, başka bir deyişle new anahtar kelimesi ile oluşturulup oluşturulmadığını öğrendik. Program henüz çalıştırılmış ve “Ekle” düğmesi ilk kez tıklandıysa dizi değişken null olacaktır. Dizi değişken null ise tanımlayıp 1 elemana sahip olmasını sağlayıp struct’ın örneğini ilk elemanına aktardık. 

Dizi değişken null değilse, başka bir deyişle “Ekle” düğmesi 2. veya 3. kez tıklanıyorsa “Dizi” adını verdiğimiz değişkenin içeriğini “Temp_dizi” adlı diğer değişkene aktarıp “Dizi” değişkeni yeniden hazırlayıp Temp_dizi’nin bütün elemanlarını aktardık. Oyuncu ekleme işlemi tamamlandıktan sonra tıklanacak “Aktar” düğmesi için yazdığımız kod aşağıdadır. 

private void Aktar_Click(object sender, EventArgs e) 
 
    Form2 F = new Form2(); 
    int sayi= Dizi.Length; 
    for (int i = 0; i < sayi; i++) 
     
        F.listBox1.Items.Add(Dizi[i].ad); 
        F.listBox2.Items.Add(Dizi[i].boy.ToString()); 
        F.listBox3.Items.Add(Dizi[i].takim); 
     
    F.Show(); 
  } 

Form2’deki ListBox’lara Form1’den direk ulaşabilmek için her üçünü de public yaptık. Birkaç basketbol oyuncusunun adını, boyunu ve takımını girip “Aktar” düğmesini tıklayınca aşağıdaki gibi bir sonuç aldık.



Enum – Sayılabilir Tipler 

Programcılar programın işletimi sırasında bilgi saklamak üzere daha önce anlatılan hazır tipleri kullandıkları gibi kendi tiplerini hazırlayıp kullanabilirler. Programcılar kendi tiplerini hazırlarken daha çok Class’ları, yapıları(Struct), dizi değişkenler ve Enum’ları tercih ediyorlar. Bu arada .NET Framework ile birlikte çok sayıda Enun tipinin, başka bir adlandırma ile numaralayıcının hazır olarak geldiğini belirtmek gerekir. 

Küçük veriler söz konusu olduğunda sınıf(class) yerine yapı(struct) kullanmak daha pratik ve verimli olabilmektedir. Bu nedenle yapının yeterli olduğu durumlarda tercihinizi yapıdan yana kullanmanız önerilir. Bu kısımda Enum(sayılabilir) tipler hakkında bilgi verilecektir. Örnek vermek gerekirse Windows’un her mesajının bir numarası bulunmaktadır. Hangi numaralı mesajın hangi işlemi temsil ettiğini programcı için akılda tutmak zor olduğu için mesajlara işlevini çağrıştıran adlar verip bunları Enum olarak bir CS dosyasına yazıp her uygulamanızda kullanıp gerek duyduğunuz mesajı kolayca hatırlayabilirsiniz.



Sayılabilir tipler söz konusu değişkenin alabileceği değerler önceden belli ise işlevsel olmaktadır. Bu tip bilgilere haftanın günleri ve mevsimler örnek olarak verilebilir. Başka bir anlatımla, sayılabilir tipler, sayısal değerleri anlamlı adlarla ifade etmemize yardımcı olmaktadır. Aşağıda verilen kodda “Mevsimler” adında bir enum tipi tanımladık. Enum tiplerini tanımlarken public, private ve protected kapsam belirteçlerini kullanabilirsiniz.



Bu şekilde “Mevsimler” adında enum tipini hazırladıktan sonra bu tipten yararlanarak değişken tanımlayıp kullanabilirsiniz. Aşağıda “Mevsimler” tipinden yola çıkarak “Bu_Mevsim” adında bir değişken tanımladık. “Bu_Mevsim” değişkenine bilgi aktarırken Visual Studio aktarabileceğimiz seçenekleri listeledi.



Şimdi “Mevsimler” tipindeki “Bu_Mevsim” değişkenin içeriğini int tipinde bir değişkene aktaracağız. Aktarma işlemi sırasında dönüştürme yapılmazsa hata meydana gelir. “Mevsim” değişkenine “Kış” bilgisini aktardığımız için “Sira” değişkenin içeriği 3 olur.



Bu metot işletildiğinde ilk TextBox’a “Kış” ve 2. TextBox’a ise 3 aktarılır. Çünkü “Kış” seçeneğinin enum tipi içindeki sıra numarası 4’tür. İlk elemanın sıra numarası 0’dan başlamaktadır. “Mevsimler” tipindeki değişkene “Yaz” bilgisini aktarmak istiyorsanız aşağıdaki gibi bir satır yazabilirsiniz. 

Bu_Mevsim = (Mevsimler)1; 

Yukarıda tanımlayıp “Mevsimler” adını verdiğimiz enum tipinde ilk olarak yazılan elemana(ilkbahar) 0, 2. sırada yazılan elemana ise 1 sıra numarası verilir. İsterseniz elemanlara kendiniz sıra numarası verebilirsiniz. Bu nedenle “Mevsimler” adını verdiğimiz Enum tipi aşağıdaki gibi tanımlanırsa değişen bir şey olmaz.
 

public enum Mevsimler 
 
   ilkbahar = 0, 
   yaz = 1, 
   sonbahar = 2, 
   kis = 3 
 

Enum tipinin elemanlarına verilen sıra numaralarının veya aktarılan bilgilerin int tipinde olduğu varsayılmaktadır. Kendi tanımladığınız enum tipinin elemanları fazla değilse int yerine byte veya başka bir tamsayı tipi(short, ushort, long, ulong) kullanılabilir. 

public enum Mevsimler : byte 
 
   ilkbahar=0, 
   yaz=1, 
   sonbahar=2, 
   kis=3 
 

Enum tipinin elemanlarına sayısal değer aktarırken bütün elemanlara 2. elemanın içeriği ilk elemandan 1 fazla olacaksa, başka bir deyişle elemanların içeriği sıralı bir şekilde artıyorsa ilk eleman hariç diğerlerini belirtmek zorunda değilsiniz. 

public enum Yillar 
 
   ilkyil = 2005, 
   ikinciyil, 
   ucuncuyil, 
   dorduncuyil 
 

Bu şekilde tanımlanan enum tipinde “ikinciyil” adını verdiğime elemana 2006, diğerlerine ise 2007 ve 2008 bilgileri aktarılmış varsayılır. Bu nedenle aşağıda verilen kod işletilirse formun başlığına 2006 yazıılır. 

private void button1_Click(object sender, EventArgs e) 
 
   Yillar buyil = Yillar.ikinciyil; 
   int yil = (int)buyil; 
   this.Text = yil.ToString();  
 

Enum değişkeninde saklanan bilginin tipini öğrenmek istiyorsanız GetTypeCode() metodundan yararlanabilirsiniz. Bu metodun nasıl kullanıldığını aşağıda görebilirsiniz. Bu kod işletildiğinde forma yerleştirilen TextBox’a Enum’un tipi aktarılır. Enum’un tipi int ise TextBox’a “Int32”, byte ise “Byte” aktarılır. 

private void button1_Click(object sender, EventArgs e) 
 
   Mevsimler Bu_mevsim; 
   Bu_mevsim = (Mevsimler)2; 
   textBox1.Text = Bu_mevsim.GetTypeCode().ToString(); 


Konunun iyice anlaşılmasını sağlamak için başka bir örnek hazırlayacağız. Bu amaçla ilk olarak “Aylar” adından bir enum tipi hazırladık. 

public enum Aylar 
 
    Ocak=1, Şubat=2, Mart=3, 
    Nisan=4, Mayıs=5, Haziran=6, 
    Temmuz=7, Ağustos=8, Eylül=9, 
    Ekim=10, Kasım=11, Aralık=12 
 

Proje çalıştırıldığında geçerli tarihe uygun olarak ay bilgisinin forma yerleştirdiğimiz Label’a yazılmasını sağlamak üzere formun Load olayını temsil eden metodu aşağıdaki gibi düzenledik. 

int Ay;
private void Form1_Load(object sender, EventArgs e) 
 
   DateTime Tarih; 
   Aylar Bu_ay; 
   Tarih = DateTime.Today; 
   Ay = (int)Tarih.Month; 
   Bu_ay = (Aylar)Ay; 
   this.label1.Text = Bu_ay.ToString();    
 

Diğer metotlarda kullanmak üzere “Ay” adında ve int tipinde bir değişkeni bu metodun dışımda tanımladık. Bu kodda önce geçerli tarihin ay bilgisini öğrenip “Ay” adlı değişkene aktardık. Ardından Enum tipinden ay ilgili ay bilgisini öğrenip Label’a yazdık. Aşağıda verilen ekran görüntüsünü ilk formunun Load olayını temsil eden metodu bu şekilde düzenlenen projeyi çalıştırdıktan sonra aldık.



Bu sırada “Sonraki” veya “Önceki” başlıklı düğmeler tıklandığında label nesnesine yazılan ay bilgisinin değişmesini sağlamak için aşağıdaki gibi kod yazdık. Benzer kodu “Önceki” düğmesi için yazabilirsiniz. 

private void Sonraki_Click(object sender, EventArgs e) 
 
   Aylar Bu_ay; 
   Ay += 1; 
   Bu_ay = (Aylar)Ay; 
   this.label1.Text = Bu_ay.ToString();    
 

Enum Sınıfı 

Sayılabilir tipler üzerinde işlem yaparken System’de bulunan Enum sınıfının metotlarından yararlanabilirsiniz. Bu sınıftaki metotların nasıl kullanıldığını anlatmak için “Aylar” adında bir enum tipi tanımladık. Bu sayılabilir tipi hazırlamadan önce üzerinde çalıştığımız projeye içeriği boş bir Code dosyası dahil edip Enum’un bu kod dosyasında yer almasını sağladık.



Bu şekilde enum tipi tanımladıktan sonra projedeki ilk formun Click olayını temsil eden metodunda string tipte bir dizi değişken tanımlayıp GetNames() metodu ile enum tipinin elemanlarını bu dizi değişkene aşağıdaki gibi aktardık. En son olarak bu dizi değişkenin içeriğini ListBox’a aktardık.
GetNamesmetodu ile dizi değişkene aktarılan elemanları elde ederken for yerine foreach döngüsünden yararlanabilirsiniz. 

private void Form1_Click(object sender, EventArgs e) 
 
   string[] Ay_dizi = Aylar.GetNames(typeof(Aylar)); 
   for (int i = 0; i < 12; i++) 
      listBox1.Items.Add(Ay_dizi[i]);  
 

Enum tipinin bütün elemanları yerine yalnızca bir elemanı ile ilgileniyorsanız GetNames() yerine GetName() metodunu kullanabilirsiniz. İlgilendiğiniz elemanın sıra numarasını GetName() metoduna 2. parametre olarak vermeniz gerekir. Bu metodun nasıl kullanıldığını aşağıda görebilirsiniz. 

private void button1_Click(object sender, EventArgs e) 
 
   string ay = Aylar.GetName(typeof(Aylar), 3); 
   this.Text = ay;   // nisan 
 

Şimdi daha işlevsel bir enum tipi tanımlayacağız. Bazı kamu kuruluşlarında elemanlar sicil numaraları ile anılırlar veya belli sayıda ürünün satışını yapan firmalar ürünün adı yerini kodunu kullanırlar. Bu gibi durumlarda kişinin sicil numarasını veya ürünün kodunu yazmak zorunda kalmamak için enum tipi tanımlayabilirsiniz. 

Yukarıda enum tipi tanımlarken elemanları temsil edecek sayısal bilgileri belirtmedik. Bu değerlerin int tipinde olacağı varsayılmaktadır. Elemanlara sıfırdan başlayan ve ardışık değerlerin aktarılmasını istemiyorsanız elemana istediğiniz değeri aktarabilirsiniz. 

enum iller : byte

  Adana = 1, 
  Ankara = 6, 
  Antalya = 7, 
  Bursa = 16, 
  Eskişehir = 26, 
  Gaziantep = 27, 
  İstanbul = 34, 
  İzmir = 35


İllerin plaka numaralarını girmek yerine adlarını bir liste kutusundan seçmek için enum tipini tanımladıktan sonra projenin ilk formuna bir ComboBox yerleştirip bu formun Load olayını temsil eden metodu aşağıdaki gibi düzenledik. 

private void Form1_Load(object sender, EventArgs e) 
 
   string[] il_dizi = iller.GetNames(typeof(iller)); 
   foreach (string il in il_dizi) 
     comboBox1.Items.Add(il);   
 

Bu satırlar sayesinde sayılabilir tipin elemanları ComboBox’ta listelenir. Elemanları “Bursa=16” gibi tanımlamış olmama rağmen Combobox’ta yalnızca il adları listelenir. ComboBox’ta seçilen ilin plakasını göstermek için forma bir TextBox yerleştirip ComboBox’ta seçim yapıldığında işletilecek metodu aşağıdaki gibi düzenledik.



Fazla pratik değeri olmayan bu kodu konu anlaşılsın diye hazırladık. Bu şartlarda proje çalıştırılıp ComboBox’ta herhangi bir il seçilirse aşağıdaki gibi sonuç alınır.



Programcı kod dahilinde “plaka==27” yazacağına plaka== (
byte)iller.Gaziantepyazacaktır. Bu sayede kod daha okunur olacaktır. Buradaki byte enum’un veri tipini işaret etmektedir. Enum sınıfının Parse() metodu olmasaydı yukarıda verilen koda ekleme yapmaz veya üzerinde konuşmayı burada keserdik. Enum sınıfının Parse() metodunun nasıl kullanıldığını aşağıda görebilirsiniz. 

private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) 
 
   string secilen= comboBox1.Text; 
   byte plaka = (byte)Enum.Parse(typeof(iller), secilen, true); 
   textBox1.Text = plaka.ToString();  
 

Parse() metodu kendisine 2. parametre olarak verilen bilgiyi 1. parametrede verilen Enum tipinde aramaktadır. Parse() metodunun bool tipte olan 3. parametresi Enum’da arama yapılırken küçük büyük harf ayrımının yapılıp yapmayacağı ile ilgilidir. 3. parametre olarak false kullanılırsa aramada küçük büyük harf ayrımı yapılır. Bu kod sayesinde ComboBox’ta seçilen ilin plakası TextBox’a aktarılır. Parse() metodu ile Enum tipte aranan bilgi bulunamazsa hata meydana gelir. 

Format Metodu 

Enum sınıfının yukarıda sözü edilen Parse() metodundan başka ayrıca Format() metodu bulunmaktadır. Bu metot sayesinde enum tipinin elemanlarını biçimleyebilirsiniz. Bu metodun nasıl kullanıldığını anlatmak için aşağıda verilen kodu yazdık.   

private void Form1_Click(object sender, EventArgs e) 
 
   iller il = iller.Adana; 
   textBox1.Text = il.ToString(); 
 } 

Tahmin edeceğiniz gibi bu kod işletildiğinde TextBox’a “Adana” yazılır. Tabii TextBox’a Adana yerine bu ilin Enum’daki sıra numarasının yazılmasını istemiş olsaydık Parse() metodunu aşağıdaki gibi kullanırdık. 

byte plaka = (byte)Enum.Parse(typeof(iller), "Adana", true);
textBox1.Text = plaka.ToString();    

Ne var ki bu kullanım bazen pratik olmaz. Örneğin TextBox’a 1 yerine “01” yazmak istenebilir. Bu gibi durumlarda Enum sınıfının Format() metodunu kullanmak gerekir. Bu metodun nasıl kullanıldığını aşağıda görebilirsiniz. 

private void Form1_Click(object sender, EventArgs e) 
 
   iller il = iller.Adana ; 
   string str = Enum.Format(typeof(iller), il, "G"); 
   textBox1.Text =str; 
 

Bu kodda önce kendi hazırlamış olduğumuz enum tipinde bir değişken tanımlayıp “Adana” adlı elemanı bu değişkene aktardık. Ardından bu hazırladığımız enum tipindeki(il) değişkeni Format metoduna 2. parametre olarak verdik. Format() metodunun 1. parametresi Parse() metodunda olduğu typeof() operatörü ile elde edilen tiptir. Format() metoduna 3. parametre olarak “G” karakterini verdik. Bu şekilde düzenlenen Format() metodu geriye “Adana” bilgisini gönderir. Format() metodunun geriye söz konusu elemanın enum’daki sırasını geri göndermesini istemiş olsaydık aşağıdaki gibi kullanırdık. 

iller il = iller.Adana ;
string  str = Enum.Format(typeof(iller), il,  "D");
textBox1.Text = str; 

Bu kod işletildiğinde TextBox’a “1” yazılır. TextBox’a 1 yerine “01” yazmak isteseydik Format() metoduna 3. parametre olarak “X” karakterini verirdik. 

iller il = iller.Adana ;
string str = Enum.Format(typeof(iller), il, "X");
textBox1.Text = str; 

GetValues Metodu 

Yukarıdaki sayfalarda Enum sınıfının GetNames() metodundan yararlanıp ilgili Enum’daki bilgileri veya adları elde edip ListBox’a aktarmıştık. Daha sonra programcı seçtiği enum sabitinden yola çıkıp o sabitin temsil ettiği değeri bulup ona göre işlem yapmıştık. Enum sınıfının GetValues() metodundan yararlanıp enum sabitinin değerlerini elde edebilirsiniz. 

Bu metodun nasıl kullanıldığını anlatırken yukarıda verilen “iller” adını verdiğimiz sayılabilir tipten yararlanacağız. Forma 2 ListBox yerleştirdikten sonra aşağıda verilen kodu yazdık. 

private void Form1_Load(object sender, EventArgs e) 
 
   string[] il_dizi = iller.GetNames(typeof(iller)); 
   foreach (string il in il_dizi) 
      listBox1.Items.Add(il); 
   int sayi = il_dizi.Length;  
   Array plaka_dizi=Array.CreateInstance(typeof(byte), sayi); 
   plaka_dizi = iller.GetValues(typeof(iller)); 
   foreach (byte plaka in plaka_dizi) 
     listBox2.Items.Add(plaka.ToString() ); 
 

Bu kod sayesinde ilk ListBox’ta enum dahilindeki iller, 2. ListBox’ta ise illerin plakaları listelenir. Enum sınıfının GetValues() metodu geriye Array nesnesi gönderdiği için önceden bir dizi değişken tanımladık. 

Enum’da Veya Operatörünü Kullanmak 

.NET Framework ile gelen bir çok Enum tipinde içinde bulunulan şartlara göre “veya” durumları söz konusu olabilmektedir. Kendi hazırladığınız Enum tipte farklı seçimler yapılsa bile aynı işlemi yaptırtmak isteyebilirsiniz. Örneğin söz konusu kişinin öğrenim durumunu seçerken hazırladığınız sayılabilir tipte, hem lisans hem de yüksek lisans seçeneklerine aynı işlevi yükleyebilirsiniz. Bunu örneklemek için aşağıdaki gibi bir Enum hazırladık. 

public enum ogrenim_durumu :byte 
 
   ilk = 1, 
   orta = 2, 
   lise = 3, 
   lisans = 4, 
   yuksek_lisans = 5, 
 

Bu şekilde enum tipi hazırladıktan sonra aşağıdaki kod yazarak ilgili öğrenim durumu seçeneğinin sıra numarasını TextBox’a aktardık. Aşağıda verilen kodda sizin için herhangi bir yenilik yoktur. 

private void Form1_Click(object sender, EventArgs e) 
 
   ogrenim_durumu ogrenim = ogrenim_durumu.ilk; 
   byte sira = (byte)ogrenim; 
   textBox1.Text = sira.ToString(); 
 

Bu kod işletildiğinde TextBox’a 1 yazılır. Eğer öğrenim durumu ilk ve orta okul olanları aynı grubu dahil etmek istiyorsanız bu kodu aşağıdaki gibi yazabilirsiniz. 

private void Form1_Click(object sender, EventArgs e) 
 
   ogrenim_durumu ogrenim = ogrenim_durumu.ilk | ogrenim_durumu.orta;   
   byte sira = (byte)ogrenim; 
   textBox1.Text = sira.ToString(); 
 

Tahmin edeceğiniz gibi bu kod işletildiğinde TextBox’a 3 yazılır. Ne ki hazırladığımız Enum’da 3 ile öğrenim durumu lise olanlar işaret edilmektedir. Bu nedenle “veya” kullanılacak enum sabitlerinin seçeneklerine sabit değer verilirken dikkatli olmak gerekir. Bizim örnekte en basit şekli ile öğrenim durumu ilk ile orta olan toplanır ki 3 değeri elde edilir. 

Buna göre 3. sıradaki “Lise” öğrenim seçeneği için en az 4 değeri seçilmelidir. 4. sıradaki seçeneği temsil edecek sayısal değer ilk üçünün toplamından en azından 1 fazla olmalıdır. Enum aşağıdaki gibi düzenlenirse yukarıda sözünü ettiğimiz sorun yaşanmaz. 

public enum ogrenim_durumu : byte 
 
   ilk = 1, 
   orta = 2, 
   lise = 4, 
   lisans = 8, 
   yuksek_lisans = 16, 
 

Bu durumda 2 ortayı, 3 ise “ilk veya ortayı”, 5 “ilk veya liseyi”, 6 “orta veya liseyi” temsil eder. Bu nedenle aşağıda verilen kod işletilirse TextBox’a 7 yazılır. 

private void Form1_Click(object sender, EventArgs e) 
 
   ogrenim_durumu ogrenim= ogrenim_durumu.ilk | 
              ogrenim_durumu.orta | ogrenim_durumu.lise ;   
   byte sira = (byte)ogrenim; 
   textBox1.Text = sira.ToString(); 
 

Enum sınıfının yukarıda sözü edilen özellik ve metotlarından başka ayrıca Enum’da arama yapılırken kullanılan IsDefined() adında bir metodu var. Bu metot sayesinde sözü edilecek seçeneğin Enum tipinde yer alıp almadığı öğrenilmektedir. 

private void Form1_Click(object sender, EventArgs e) 
 
   bool sonuc = Enum.IsDefined(typeof(ogrenim_durumu), "lise");   
   textBox1.Text = sonuc.ToString(); 
 

IsDefined() metodunun ilk parametresi ile aramaya konu edilecek enum tipi işaret edilirken 2. parametrede aranmak istenen seçenek verilmektedir. Yukarıda “ogrenim_durumu” adını verdiğimiz enum’da “lise” adında bir seçenek olduğu için bu kod işletildiğinde IsDefined() metodu geriye true gönderir. 

Şimdi enum’ların başka bir özelliği hakkında bilgi vermek için 4 elemana sahip yeni bir tipi hazırladık. Bu enum örneğinde “hepsi” seçeneğinin sıra içeriğini doğrudan yazmak yerine veya operatöründen yararlandık. 

public enum diller : byte 
 
   ingilizce = 1, 
   almanca = 2, 
   fransizca = 4, 
   italyanca = 8, 
   hepsi = ingilizce | almanca | fransizca | italyanca 
 

Aşağıda verdiğim kodu dikkatlice incelerseniz “hepsi” elemanın 15 değerini içeriğini tahmin edebilirsiniz. Bu nedenle bu kod işletilirse TextBox’a 15 yazılır. 

       
private void Form1_Click(object sender, EventArgs e) 
         
           diller dil = diller.hepsi; 
           byte sira = (byte)dil; 
           textBox1.Text = sira.ToString(); 
         }

* Daha önce satın aldığınız C# kitaplarında Struct ve Enum’lar hakkında bu kadar bilgi yoksa hemen kitapların yazarlarını suçlamayınız. Hele hele bu yazarlar değişken adı sahibi olacak kadar zirvelere çıkmışlarsa kendilerini taktir etme borcunuzun olduğunu lütfen unutmayınız.

C# 3.0 Kitabı | Bu yazıya 6 yorum yapılmış. | 09.12.2009 23:21:54

Ana Sayfa | Hakkımda | Kitap Önerileri | Fotoğraflarım | RSS | İletişim
Memik Yanık 2004-2009 © Tüm Hakları Saklıdır.
Hazırlayan www.semgoksu.com