|
Hata ayıklayıcınızı (debugger) tanıyormusunuz? |
|
Gönderiliyor lütfen bekleyin... |
|
|
Uygulamarımızı geliştirirken heralde en çok yaptığımız işlemlerin başında hata ayıklama (Debug) işlemleri geliyor.
Gerek uygulamamızın hata verdiği durumlarda gerekse uygulamamızdan beklenmedik
değerler aldığımızda hatta programlamaya yeni başlayanlar kodların nasıl
çalıştığını daha iyi anlayabilmek adına debug işlemleri programcı için büyük
kolaylıklar getirmektedir.
Bu makalemizde debug işlemleri yaparken gözden
kaçan, aslında yeri geldiğinde işlerimizi büyük oranda kolaylaştıran bazı
özelliklere değinip, yeri geldiğinde de debug işlemlerimizi özelleştirmemize
yardımcı olacak .Net Frameworkün bize sağladığı imkanlar üzerinde duracağız.Bu
özellikler sayesinde debug işlemlerimizin gücünü biraz daha arttırmış olacağız.
İlk olarak debug işlemleri sırasında bir dizinin değerlerini görüntülemek
istediğimizde aşağıdaki (resim-1) gibi bir görüntü ile karşılaşırız.
(resim-1)
Oysaki biz
dizi elemalarının Type bilgisini (namespace.class_name)
görmek yerine daha anlamlı bir veri görmek isteriz. Aslında biliyoruz ki bu bilgi ilgili nesnenin
ToString metodunun çağrılması sonucu elde edilmiştir. Bu durumda nesnemizin
kodları üzerindeki küçük bir
değişiklikle aşağıdaki gibi daha anlamlı ve debug işlemi sırasında zaman
kazandıracak bir görüntüyü sağlayabiliriz.
public override string ToString()
{
return Name;
} |
Artık debug işlemi sırasında dizi
elemanlarımız görüntülenmek istendiğinde ilgili nesnenin ToString()
metodu çağrılacak ve sonuçta bizim istediğimiz (override işlemi
sonucunda) çıktı debug ekranında (resim-2)belirecektir.
(resim-2)
Görüldüğü gibi küçük işlemler ile daha kolay ve anlaşılır debug işlemleri yapabilmekteyiz.
Şimdi de debug işlemleri sırasında işlerimizi kolaylaşatıracak .net framework içindeki
bazı niteliklere (Attribute) değinelim.Örneklerimizin açıklamasında
kullanacağımız - anlaşılır olabilmesi için - basit bir sınıf örneği
olarak Ogrenci class'ını yazalım.
[DebuggerDisplay("Ad ={Name},No = {No}")]
public class Ogrenci
{
private string _name;
public string Name
{
[DebuggerStepThrough]
get { return _name; }
[DebuggerStepThrough]
set { _name = value; }
}
private int _no;
public int No
{
get { return _no; }
set { _no = value; }
}
private int _x;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
public int X
{
get { return _x; }
set { _x = value; }
}
} |
İlk olarak
debug işlemi esnasında büyük nesnelerimizin özelliklerini incelemek istediğimizde, nesne üzerine gelip
ilgili değerleri arayıp bulur ve inceleriz. Ama aslında biz sadece 2 ya da 3
özelliğin değerini incelemek istediğimizde onca özellik arasından gidip o
değerleri bulmak hem sıkıcı hem de zaman alıcı iştir. Tam da bu gibi
durumlar için DebuggerDisplay niteliği (Attribute) biçilmiş
kaftandır. Debug işlemi sırasında nesne üzerine geldiğinizde sadece istediğiniz
özelliklerin gösterilmesini sağlar.
Örneğimizde Ogrenci sınıfımıza DebuggerDisplay
niteliği uygulanarak debug işlemi sırasında nesne üzerine geldiğimizde sadece
Name ve No özelliklerinin (property) istediğimiz bir string formatında
görüntülenmesi sağlanmış, sınıfımızın diğer bir üye elemanı olan X özelliği ise
devre dışı bırakılmıştır.Tabi ki görüntülenmek istendiğinde nesnenin detayına bakılarak değeri öğrenilebilir.
Bu niteliği uygulamadığımız sınıfdan bir nesne debug sırasında aşağıdaki
(resim-3) deki gibi görünecektir.
(resim-3)
Diğer bir niteliğimiz ise DebuggerStepThrough;
bu nitelik sayesinde debug işlemi esnasında F11 ile dallanma
yapmak istediğimizde eğer bizim için önemsiz bir kod bölümü varsa bu
kısma dallanma yapmak istemeyeceğimizi belirtebiliriz. Örneğin
Ogrenci o =
new
Ogrenci();
o.Name = "ali";
o.No = 22;
|
gibi bir kodu debug ettiğimizi
düşünürsek o.Name="ali"; satırında iken F11 e bastığımızda
Name özelliğinin set metoduna dallanacağını biliyoruz. Fakat
DebuggerStepThrough niteliği set için uygulandığından bu dallanma gerçekleşmeyecek
o.No=22; kod satırından itibaren debug işlemi devam edecektir.
Gerçekten hoş bir nitelik değil mi sizce de.
Diğer niteliğimiz DebuggerBrowsable niteliği.Isminden de açıkça belli olduğu üzere bu
niteliğin uygulanmış olduğu nesnenin özelliği debug ekranında görüntülenmeyecektir.
Örneğimize bakacak olursak X özelliğinin debug işlemi
sırasında görüntülenmesini istemediğimiz için DebuggerBrowsable
niteliğine DebuggerBrowsableState.Never değerini
veriyoruz.( Verilebilcek diğer değerler için MSDN'den detaylı bilgi
alabilirsiniz.)Çıktımız aşağıdaki resimdeki (resim-4) gibi olacaktır.
(resim-4)
Debug işlemleri sırasında daha etkili
debug yapmak ya da belirli durumlar gerçekleştiğinde debug
işlemlerimizin geçerli olabilmesi için breakpoint'i
biraz daha detaylandırmak gerekecektir. Herhangi bir breakpoint üzerine
sağ tıkladığınızda breakpoint üzerine çeşitli işlemler yapabileceğiniz
bir menü ile karşılaşacağız. Şimdi bu özellikler ile neler
yapabileceğimize bir bakalım.Yine basit ve anlaşılır bir sınıf üzerinde
incelememize devam edelim.
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void button1_Click(object sender, EventArgs e)
{
DoOp();
}
public void DoOp()
{
Thread th = new Thread(Do);
th.Name = "debugTest";
th.Start();
}
int dummyValue = 22;
public void Do()
{
for (int i = 0; i < 5; i++)
{
Text = i.ToString(); //Breakpoint konulan satır
}
}
|
İlk bahsedeceğimiz Condition tanımlama işlemi. Eğer belirli bir
şarta bağlı olarak debug işlemini gerçekleştirmek istediğimizde bu özelliği aktif hale getirebiliriz.
Örnek kodumuz üzerinden açıklamaya çalışırsak; Do metodunda
dummyValue değişkenin değeri 22 ise debug işleminin aktif hale gelmesini istiyorum.
Bu durumda ilgili breakpoint üzerine sağ tıklayıp çıkan menüden
Condition... u seçip aşağıdaki resimdeki (resim-5) gibi bir şart( Condition )
ekliyorum.
(resim-5)
Artık bu işlemden sonra sadece dummyValue değişkenimin değeri 22 ise breakpoint aktif olacak aksi halde
yokmuş gibi varsayılacaktır.
breakpoint üzerinde tanımlayabileceğimiz diğer işlem ise Hit Count
işlemidir. Debug işlemi sırasında o ana kadar kaç defa breakpointe gelindiğini takip
etmek ve eğer istersek de "eğer bu breakpointe x defa gelindiğinde artık debug işlemi yapılmasın" da diyebilirsiniz. (Açılan dialog penceresindeki comboboxdan diğer seçenekleri inceleyiniz.
(resim-6))
(resim-6)
Diğer bir breakpoint işlemi olan
Filter ile belirli filitreler koyarak debug işlemlemini aktif hale getirebiliriz. Aşağıdaki resimde
(resim-7) de görüldüğü üzere MachineName,ProcecssId,
ProcessName,ThreadId ve ThreadName gibi filitreler koyabileceğimizi
görüyoruz.Bizim örneğimizdeki filitrede, eğer bu metod "debugTest" isimli Thread tarafından çağrılmışsa debug işlemi aktif hale gelsin
şeklinde bir filitre uygulandığını görüyoruz.
(resim-7)
Son olarak When Hint isimli
işlemi inceleceğiz.Debug işlemi aktif hale geldiğinde isterseniz Output Windowa durum bilgilerini yazdırabilir, isterseniz de VS.NET 2005e ait çeşitli makrolardan birini çağırabilirsiniz. Örneğimizde breakpointin hangi metod içinde olduğunu ve bu metodu kimin çağırdığını yazdırıyoruz
(resim-8). Ardından da o satırı yorum satırı haline getiren bir makroyu çalıştırıyoruz.
(resim-8)
When Hint tanımlamarımızı yapıp debug modunda uygulamamızı çalıştırdığımızda aşağıdaki gibi
(resim-9) bir sonuç aldığımızı göreceğiz.
(resim-9)
Son inceleyeceğimiz konu ise
DebuggerTypeProxy ile iç içe geçmiş nesnelerimizi debug işlemi
sırasında özelleştirilmiş şekilde görüntülemek olacak. Durumu senaryolaştırmak
için aşağıdaki örnek üzerinde açıklamalarda bulunacağız.
[DebuggerTypeProxy(typeof(ReportDebuggerProxy))]
public class Report
{
private DateTime _date;
public DateTime ReportDate
{
get { return _date; }
set { _date = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
private List _results = new List();
public List Results
{
get { return _results; }
set { _results = value;}
}
private int _dummy;
public int Dummy
{
get { return _dummy; }
set { _dummy = value; }
}
} |
Debug şlemleri yaparken sıklıkla karşılştığımızın durumların başında nesnemiz başka bir nesneyi içeriyorsa bu nesneyi ancak hiyeraşik bir biçimde inceleme şansına sahip oluyoruz. Aşağıdaki resimde
(resim -10) daha net birşekilde ne demek istediğim belli oluyor.
(resim-10)
Görüldüğü üzere Report türünden r nesnemiz oluşturulmuş ve Results özelliğine 4,2 ve 11
değerleri eklenmiştir. Bu durumda debug sırasında Results üyesinin içeriğini görüntülemek için + resmine tıklayıp değerlerini
incelemek gerekir.
Bu özellik başka bir nesneyi temsil ediyor, ve o nesne de içerisinde iç içe (nested ) başka değerleri tutuyor olabilir
(Dataset->Tables->DataRows... gibi). Bu durumda + lar ile detaylara inip boğulabilirsiniz
ki bizim örneğimizde bunları yapsak bile değerlerin Hexdecimal şeklinde olduğunu görüyoruz.
Ama çoğumuz bu iç içe nesnelerin aslında sadece işimize yarayacak kısmını debug işlemi sırasında görmek isteyebilir, hatta bu kısımları özelleştirerek istediğimiz gibi bir görüntü sağlayabiliriz. Aşağıdaki resim ile
(resim-11) bir önceki resimi (resim-10)kıyaslayarak debug işlemi sırasındaki görüntüleri karşılaştırabilirsiniz.
(resim-11)
Görüldüğü gibi aslında Report nesnemize ait sadece Name, ReportDate be Results üyelerinin değerleri
görüntülenmiş, Results iç içe değil de özel bir biçimde görüntülenmiş, Name
özelliğinin değerine "Report Name : " değeri eklenmiştir. Bunu yapabilmek için
bu özellikleri içeren ve debug ekranında bu özelliklerin geriye gönderiği
değerleri görüntülenecek bir proxy sınıfa ihtiyazcımız var.Aşağıdaki sınıfımız
ile bunu gerçekleştiriyoruz.
public class ReportDebuggerProxy
{
Report _report;
public ReportDebuggerProxy(Report report)
{
_report = report;
}
public string ReportDate
{
get { return _report.ReportDate.ToLongDateString();}
}
public string Name
{
get { return "Raport Name : " + _report.Name; }
}
public string Results
{
get
{
StringBuilder sb = new StringBuilder();
foreach (int result in _report.Results)
{
sb.Append("(");
sb.Append(result.ToString());
sb.Append(") ");
}
return sb.ToString();
}
}
}
|
Görüldüğü gibi proxy sınıfımız Report
sınıfından bir nesne örneği içermekte ve debug işlemi sırasında hangi özellikler
görüntülenecekse bunlarla aynı isimli özellikler read-only (sadece okunabilir)
olarak yazılmıştır.Report sınıfımızı yazdığımız koda dikkat ederseniz
Dummy isimli bir özellik içermesine karşın buna proxy sınıfında yer verilmemiş bu sayede de
debug işlemi sırasında bu özelliğin görüntülenmemesi sağlanmıştır.
Diğer önemli nokta ise Report sınıfındaki bu
özelliklerin debug işlemi sırasında nasıl görüntülenmesini istiyorsak proxy
sınıfımızdaki özelliklerin get metodunda geriye o şekilde değerler gönderiyoruz.
Örneğin proxy sınıfdaki Name özelliği geriye "Report Name : " +....
şeklinde değer döndererek debug işlemi sırasında nasıl
görüntüleneceğini (resim -11)kişiselleştirme olanağı sağlamış oluyor
geliştiriciye.
Tüm bu özellikleri sağlayabilmek için ise Report sınıfına
DebuggerTypeProxy niteliğini uygulamak ve
bu niteliğe de proxy sınıfın ne olduğunu bildirmek için de parametre olarak proxy sınıfımızın (ReportDebuggerProxy) tipini
paremetre olarak veriyoruz ( typeof(ReportDebuggerProxy)
)
Unutmayalım ki hataların tespitinde, bulunan hataların kısa sürede
giderilmesinde debug işlemi olmazsa olmazdır. Ve unutmayalım ki iyi debug işlemi
yapan bir programcı kendisi ve çevresi için çok zaman kazandıracaktır ;)
Oğuz YAĞMUR
[email protected]
Makale:
Hata ayıklayıcınızı (debugger) tanıyormusunuz? C#, Visual C# ve .NET Oğuz Yağmur
|
|
|
-
-
Eklenen Son 10
-
Bu Konuda Geçmiş 10
Bu Konuda Yazılmış Yazılmış 10 Makale Yükleniyor
Son Eklenen 10 Makale Yükleniyor
Bu Konuda Yazılmış Geçmiş Makaleler Yükleniyor
|
|