SİTE
İÇİ ARAMA |
|
Blogroll |
|
|
|
C#'ta Önişlemci Direktifleri |
|
Gönderiliyor lütfen bekleyin... |
|
|
Bu makalemizde, özellikle C programcılarının yakından
tanıdıkları önişlemci direktiflerinin C# tarafında nasıl yer aldıklarını
incelemeye çalışacağız. C ile daha önceden uğraşmış bir uygulama
geliştiricisi için "önişlemci" ve "önişlemci direktifi" kavramları çok da
yabancı değildir; ancak bir .NET programcısı açısından bakarsanız önişlemci
direktifleri çok da fazla kullanılmayan, hakkında fazla konuşulmayan bir
konudur. Bu yüzden işin C# tarafına geçmeden önce çok kısa bir şekilde "önişlemci"
ve "önişlemci direktifi" kavramlarına C penceresinden bakmakta fayda vardır.
Aşağıda, bir C programının çalışma sürecini gösteren şemayı incelediğimizde .NET mimarisinden farklı olarak derleyiciden önce çalışan başka bir yapı
görürüz: Önişlemci. Bu yapının görevi, en kaba tabirle, kaynak kodu derleyiciye
ulaşmadan önce düzenlemektir. Biraz daha açmak gerekirse C önişlemcisinin; yorum
satırlarının atılması, makro tanımlanması, boş karakterlerin silinmesi gibi
işleri yaptığı sayılabilir.
C önişlemcisinden kısaca bahsettiğimize göre gelelim önişlemci direktiflerine.
Önişlemci direktifleri ise kaynak kod içerisine yazdığımız ve önişlemci
tarafından ele alınarak bazı işler yapmamıza imkan veren özel direktiflerdir.
Önişlemci, bu direktifler doğrultusunda bir çıktı oluşturarak derleyiciye (
compiler ) verir. Böylece derleme işlemine yön verilebilir, ekstra bir takım
işler yaptırılabilir.
Sanırım önişlemci direktifleriyle ilgili birçok soru işaretini yok etmiş olduk;
tabi bununla birlikte birçok soru işareti de oluşturduk. Cdeki süreç tamam, ama
C# tarafında işler nasıl yürüyor olabilir? .NET mimarisi içinde de önişlemci
diye bir yapı bulunur mu? C#ta kullanabileceğimiz önişlemci direktifleri nelerdir ve
hangi amaçla kullanılırlar? Yazının geri kalanında bu ve bunun gibi sorulara
cevap vermeye çalışacağız.
Öncelikle C#ta kullanabileceğimiz önişlemci direktiflerini
kullanım amaçlarına göre listeleyelim. Daha sonra bu direktiflerin her birini
kısa kısa örneklerle mercek altına alalım.
Koşullu derleme |
#define |
#undef |
#if |
#else |
#endif |
#elif |
Derleme |
#error |
#warning |
#line |
#pragma |
Görsel |
#region |
#endregion |
|
|
C#ta önişlemci
direktifleri her ne kadar "önişlemci direktifleri" olarak
adlandırılsalar da aslında bu isimlendirme yüzde yüz doğru değildir.
Çünkü, .Net mimarisinde önişlemci diye bir kavram bulunmamaktadır.
Ancak, alışılagelen önişlemci direktifleri gibi kullanıldıkları ve aynı
davranışları gösterdikleri için bu şekilde bir adlandırmaya da yanlış
denilemez. |
A)Koşullu Derleme
a)Nedir?
Temel çalışma mantığı if-else karar yapısından
farklı olmamakla birlikte, #if-#endif anahtar kelimeleriyle tanımlanmış bir kod
bloğu, yalnızca koşul sağlandığı taktirde derlenir. Örnek olarak aşağıdaki
kodu inceleyelim.
static void Main(string[] args)
{
Console.WriteLine("TEST");
#if true
Console.WriteLine("Kosul saglandi");
#else
Console.WriteLine("Kosul saglanmadi");
#endif
} |
Programın CIL çıktısına baktığımızda, koşulun
sağlanmadığı durumlarda çalışacak olan kodun derlenmediğini görürürüz.
.method private hidebysig static void
Main(string[] args) cil managed
{
.entrypoint
// Code size 24 (0x18)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "TEST"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: ldstr "Kosul saglandi"
IL_0011: call void [mscorlib]System.Console::WriteLine(string)
IL_0016: nop
IL_0017: ret
} // end of method Program::Main |
Ancak burada #if direktifinin kullanımındaki
kısıtların altını çizmek gerekmektedir. Tipik if karar yapısında, parantez içerisinde bool tipinden bir değişken
kullanabilirken, #if önişlemci direktifinde böyle bir imkanımız
yoktur. Benzer şekilde, #if direktifinde koşul olarak a<b, 1==1 gibi
ilişkisel operatörlerin kullanımları da mümkün değildir. Peki öyleyse bu direktifi yalnızca true ya da
false anahtar kelimeleriyle mi kullanabiliriz?
Tabi ki hayır. Zaten takdir edersiniz ki bu kullanım çok da işe yarar
görünmüyor. Bunun yerine, yine bir önişlemci direktifi olan #define ifadesinden
faydalanarak kendi koşullarımızı tanımlayabilir ve bu koşulları #if ile birlikte
kullanabiliriz.
|
if(true)
Console.Write("a");
else
Console.Write("b");
Normal if kullanımında da gerçeklememesi kesin olan
satırlar ( Unreachable code ) programın IL çıktısında yer almaz. |
b) #define önişlemci direktifi nasıl kullanılır?
#if ifadesinde kullanmak üzere koşul tanımlamak için #define direktifinden
faydalanırız. Örnek kullanım için aşağıdaki kodları inceleyelim.
#define KONTROL
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#if KONTROL
Console.Write("Kosul ");
Console.WriteLine("saglandi");
#else
Console.WriteLine("Kosul saglanmadi");
#endif
}
}
} |
Yukarıdaki kaynak kodu çalıştırdığımızda, KONTROL
koşulunun daha önce tanımlanmış olmasından ötürü #if-#else içerisinde kod bloğu
işletilecektir.
Dikkat etmek gereken nokta, koşulun tanımlandığı yerin tüm kodun üzerinde (
using bloğunun da) oluşudur. Aksi taktirde kaynak kod derlenmez.
c)Farklı "koşul tanımlama" yolları
1-Proje özelliklerinde koşul tanımlama
#define önişlemci direktifi dışında, proje
özelliklerinden faydalanarak da koşul tanımlaması yapılabilir.
Bunun için proje özellikleri altındaki Build seçeneğinde,
"Conditional compilation symbols" yazan yere derleme koşullarını söylemek gerekir.
Birden fazla koşul tanımlaması yapılırken, koşullar arasında bir
boşluk bırakılabilir,virgül ya da noktalı virgül konabilir. Örnek olarak aşağıdaki gibi bir koşul tanımlamasının
sonuçlarını inceleyelim.
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#if CENK
Console.WriteLine("CENK kosulu tanimli");
#endif
#if OZDEMIR
Console.WriteLine("OZDEMIR kosulu tanimli");
#endif
// Tanımlanan kosullar iki
kelimeden olusamazlar.Dolayısıyla asagidaki kod hata üretir.
//#if CENK OZDEMIR
//
Console.WriteLine("CENK OZDEMIR kosulu tanimli");
//#endif
}
}
} |
2-Komut satırında koşul tanımlama
Komut satırından csc.exeyi kullanarak yaptığımız derleme
sırasında da koşul tanımlama imkanımız vardır. Bunun için, derlenmesini
istediğimiz kaynak kod dosyasının adını söyledikten sonra /define anahtarından
faydalanmak gereklir.
Test.cs |
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#if KOSUL1
Console.WriteLine("KOSUL1 tanimli");
#endif
#if KOSUL2
Console.WriteLine("KOSUL2 tanimli");
#endif
}
}
} |
Daha sonra kaynak kodumuzu komut satırından aşağıdaki gibi derleyip çalıştıralım.
d)Tanımlanmış bir koşulu kaldırmak = #undef önişlemci direktifi
Tanımlanmış bir koşulu kaldırmak için #undef direktifinden faydalanılır. Örnek olarak aşağıdaki kodu inceleyelim.
#define DENEME
#undef DENEME
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#if DENEME
Console.WriteLine("DENEME tanimli...");
#else
Console.WriteLine("DENEME tanimli degil...");
#endif
}
}
} |
Ancak, kabul edersiniz ki kaynak kod içerisinde bir koşul tanımlayıp
kaldırmak demek, o koşulun hiç tanımlanmamış olmasıyla eşdeğerdir. Dolayısıyla
#undef direktifini işe yarar şekilde kullanabilmek için koşul tanımlamasını
kaynak kod dışında yapmak gerekir. Bu da proje özelliklerini ya da komut
satırını kullanarak yaptığımız koşul tanımlamalarını kaynak kod içerisinde
ezebileceğimiz anlamına gelir. Yani kaynak kod dışında bir koşul tanımlaması
yapılırsa, tanımlanmış koşul kaynak kod içerisinden #undef direktifi ile
kaldırılabilir.
Test.cs |
#undef IKINCI
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#if BIRINCI&&IKINCI
Console.WriteLine("BIRINCI ve IKINCI kosul tanimli");
#elif BIRINCI
Console.WriteLine("BIRINCI kosul tanimli,IKINCI kosul tanimli degil");
#elif IKINCI
Console.WriteLine("IKINCI kosul taimli,BIRINCI kosul tanimli degil");
#endif
}
}
} |
e)Conditional Niteliği ( Attribute )
Conditional niteliği metot ya da nitelik sınıflarına ( attribute classes )
uygulanarak, ilgili metot ya da niteliğin kullanılmaya çalışılığındaki
davranışını belirler. Bahsettiğimiz bu noktalarda #if-#endif kullanımı mümkün
olmadığından böyle bir niteliğe ihtiyaç duyulmuştur. Benzer şekilde #if-#endif
kullanabildiğimiz noktalarda da Conditional niteliğini kullanamayız.
|
Conditional niteliği, yalnızca
geriye değer döndürmeyen metotlar ve nitelik sınıflarıyla ( attribute
classes ) birlikte kullanılabilir. |
Aşağıdaki örneği inceleyelim.
using System;
using System.Diagnostics;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
UcgenCiz();
}
[Conditional("TEST")]
static void UcgenCiz()
{
Console.WriteLine("*\n**\n***\n****\n*****");
}
}
} |
Programın çıktısı aşağıdaki gibidir.
UcgenCiz() metodunun çalışmadığını görüyoruz. Peki metodun kendisi IL
kodunda yer almış mıdır? Test için IL Dissambler ( ildasm ) aracını
kullanarak programın IL çıktısına bakalım.
IL çıktısından da görüldüğü üzere
Conditional niteliği uygulanmış metot derlenir; ancak çağırılmaz.
B)#error, #warning, #line, #pragma
a)Derleyiciye hata ve uyarı verdirmek
Derleyicinin, derleme işlemini engelleyecek şekilde bir hata üretmesini
sağlamak için #error, derleme işlemini engellemeden bir uyarı üretmesini
sağlamak için de #warning direktifi kullanılır.
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
int sayi = 1 / 0;
}
static void Baglan()
{
#error Baglan metodu henüz
yazılmadı.
}
static bool KullaniciBul(int id)
{
#error KullaniciBul metodu
henüz yazılmadı.
}
static bool KullaniciSil(int id)
{
#warning KullaniciSil metodu
henüz yazılmadı.
}
}
} |
Yukarıdaki kodu derlemeye çalıştığımızda Error
List aşağıdaki gibi görünür.
Burada dikkatinizi çekmek istediğim nokta, #error direktifini kullandığımızda,
kodtaki diğer hataları ele alamıyor oluşumuzdur. Aslında kodumuz içerisinde üç
tane hata mevcut ( 0a bölmeye çalıştığımız satır, geriye bir şey döndürmeyen
KullaniciBul(int id) ve KullaniciSil(int id) metotları ) olmasına rağmen,
yalnızca #error ve #warning direktiflerince üretilen hata ve uyarılar ele
alınabilmektedirler.
b)#line önişlemci direktifi
1-Satır numaralarıyla oynamak
#line direktifini kullanarak kaynak kodumuzun nasıl
satırlandırılacağını söyleyebiliriz. Bu kullanıma örnek olarak da aşağıdaki kodu inceleyelim.
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#line 55 "bizimDosya"
int sayi1 = 1
/ 0; //10
#line 10 "bizimDosya" //11
int sayi1;
//12
#line 80 "bizimDosya" //13
//14
int sayi2 = 1
/ 0; //15
#line 1 "sizinDosya" //16
int sayi2;
//17
}
}
} |
Kod derlendikten sonra, hata oluşan
satırlar 10,12,15 ve 17 olmasına rağmen aşağıdaki Error List görünür.
2-Hata ayıklayıcısına
yön vermek
#line önişlemci direktifinin bir diğer kullanım alanı da
hata ayıklama sırasında içerisine girmek istemediğimiz kod blokları
oluşturmaktır.
using System;
namespace OnIslemciDirektifleri
{
class Program
{
static void Main(string[] args)
{
#line hidden
Console.WriteLine("Cenk");
Console.WriteLine("Özdemir");
#line default
Console.Write("C#");
#line hidden
Console.WriteLine(" Nedir?");
}
}
} |
Örnekteki kod içerisindeki hatalar ayıklanırken
( debug ) adım adım ilerlediğimizde, "#line hidden" satırından sonraki satırlara
geçilmez, "#line default" satırına gelindikten sonra ise normal davranışa geri
dönülür.
c) #pragma önişlemci direktifi
1- Uyarıları düzenlemek =
#pragma warning
C#ta zaman zaman kodun çalışmasına engel teşkil etmeyen uyarılarla karşılaşırız.
#pragma warning ifadesi ise bize tüm uyarıları ya da belli bir uyarıyı
kapatıp/açma, kısaca uyarıları yönetme olanağı sunar. Örnek kullanım için
aşağıdaki kodu inceleyelim.
|
Tüm uyarılarlarla
ilgili seçenekleri, proje özelliklerinde ( Project Properties ) kod
analiz ( Code Analysis ) seçeneği altında bulabilirsiniz. |
2- #pragma checksum
Aynı isme sahip kaynak kod dosyalarının hata ayıklanması ( debug ) sırasında
karışıklık yaratmaması için, ilgili pdb dosyasında bir kontrol verisi ( checksum
) bulunur. Bu özellik Visual Studio 2005 ile birlikte gelmiştir ve bahsettiğimiz
isim karmaşasının önüne geçerek, hata ayıklayıcısının ( debugger ) doğru dosyayı
kullandığının doğrulamasını sağlar. Ancak bu yöntem ASP.NET projelerinde
çalışmaz. Çünkü, kontrol verisi .aspx sayfaları için değil kaynak kod için
oluşturulur. Dolayısıyla farklı yollar altındaki aynı isimli .aspx dosyaları,
hata ayıklanması sırasında problem yaratabilir. Böyle bir durumda #pragma
checksum ifadesi ile ASP.NET projelerinde .aspx sayfaları için cheksum
oluşturulabilir.
#pragma checksum "Default.aspx"
"{3673e4ca-6098-4ec1-890f-8fceb2a794a2}" "012345678AB14232" |
C)Görsel önişlemci direktifleri: #region-#endregion
#region ifadesi, Visual Studio ile çalışırken kaynak kodları saklamak için
kullanılır. Sadece, uygulama geliştiricisine görsel anlamda kolaylık sağlar,
bunun dışında programa herhangi bir etkisi yoktur.
Örnek
Böylece, C# önişlemci direktiflerini incelemeye çalıştığımız makalemizin de
sonuna geldik. Bir dahaki
makalede görüşmek üzere, hoşçakalın.
Cenk Özdemir
Makale:
C#'ta Önişlemci Direktifleri C#, Visual C# ve .NET Cenk Özdemir
|
|
|
-
-
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
|
|
|