۱۳۸۸ اسفند ۲۶, چهارشنبه

C# Preprocessor Directives  

شاید تا به حال برایتان پیش آمده باشد که در یک پروژه بعد از پیاده سازی احتیاج پیدا کنید نسخه های متفاوتی از آن نرم افزار داشته باشید البته منظور از نسخه این است که در یک نسخه تعدادی از خطوط (Compile) شوند و در نسخه دیگر نشوند ، راهی که می خواهم معرفی کنم کمک زیادی برای حل این مشکل است (حد اقل برای ما که همینطور بود) .

شاید تا الان برای این کار کد های خودتان را (Commnet) می کردید و بعد از ساختن نسخه مورد نظر برای ساخت نسخه های دیگر کد های (Commnet) شده را (UnCommnet) می کردید و نسخه های بعدی …این کار هم غیر استاندارد است و هم دارای درصد خطای بسیار بالا و بسیاری از ایراد های دیگر و برای عبور از همه ی این موانع ما به شما استفاده از (Preprocessor Directives) را توصیه می کنیم و از این به بعد به جای (Preprocessor Directives) از (PD) استفاده می کنیم .

“دستورالعمل های پیش پردازنده” شاید این متن ترجمه مناسبی نباشد اما منظور را میرساند . این دستورالعمل ها باعث می شوند که روند (Compile) خطوط برنامه را بر اساس یک سری شرط تنظیم کنید .

حالا وقتشه که با لیست این (Preprocessor Directives) ها در #C آشنا شوید :

  • #if
  • #else
  • #elif
  • #endif
  • #define
  • #undef
  • #warning
  • #error
  • #line
  • #region
  • #endregion

خوب از اول لیست شروع می کنیم :

if# , #else , #endif , #elif :

همان کاری را انجام می دهد که همیشه انجام می داده . خطوطی که ما بین

(if#) و (endif#) قرار میگیرد در صورت نیاز شما توسط (Compiler) محسابه می شود و در غیر این صورت محسابه نمی شود . (elif#) همان (else if) خودمان است .

define# :

این (PD) تقریباً اصل مطلب حساب می شود چون زمانیکه شما با بقیه (PD) ها تنظیمات دلخواه خود را انجام داده اید با مشخص کردن (define#) تنظیمات شما مورد توجه (Compiler) قرار میگیرد و کد مورد نظر شما (Compile) میشود و در صورت نبودن (define#) کدهای شما توسط (Compiler) محسابه نمی شوند .

*نکته :

(define#) باید بالای تمام (using) های استفاده شده در فایل باشد.





// preprocessor_if.cs
#define DEBUG #define VC_V7
using System;
public class MyClass
{
public static void Main()
{
#if (DEBUG && !VC_V7)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && VC_V7
Console.WriteLine("VC_V7 is defined");
#elif (DEBUG && VC_V7)
Console.WriteLine("DEBUG and VC_V7 are defined");
#else
Console.WriteLine("DEBUG and VC_V7 are not defined");
#endif
}
}


Output :



DEBUG and VC_V7 are defined


همانطور که در بالا میبینید بعد از (define#) یک نام نیز برای آن مشخص می کنیم که بهتر است با حروف بزرگ مشخص شود مانند (DEBUG) و سپس در قسمت مورد نیاز که همان دستورات شرطی می باشد از آن استفاد می کنیم .

undef# :

کاری که (define#) انجام میدهد یعنی باعث می شود کد شما مورد توجه (Compiler) قرار بگیرد دقیقاً برعکس کنید ، وظیفه (undef#) یعنی کد شما مورد توجه (Compiler) قرار نمیگیرد و به محسابه نمی شود .

warning# , #error :

با این دو (PD) کد می توانید کاری کنید که در یکی از( Tool Windows) ها به اسم (Error List) که وظیفه نمایش (Error) ها ، (Warning) ها و (Message) ها را دارد پیغام شما را به صورت یک (Warning) یا (Error) بنا بر اینکه شما از کدام یک استفاده کرده باشید نمایش بدهد تا بعد ها بتوانید به راحتی قسمت کد مورد نظرتان را بعد از (Build) کردن پروژه پیدا کنید .




// preprocessor_if.cs
#define DEBUG
using System;
public class MyClass
{
public static void Main()
{
#warning Custom Warning
#error Custom Warning
Console.WriteLine("DEBUG and VC_V7 are not defined");
#endif
}
}


line# :

به شما اجازه می دهد تا (Compiler) را در هنگام (Compile) کردن به خطی که مورد نظر شما ست برود و بعد از گذشتن از آن خط دوباره به روال عادی خود بازگردد که در این مورد برای بازگشت به روال قبل استفاده از (line default#) این امکان را ایجاد می کند .




#line 20
#line default



(line# default) مشخص می کند که Compiler بعد از رفتن به خط (20) به کجا بازگردد .

یک استفاده دیگر هم میتوان از آن کرد که به صورت زیر می باشد :




Console.WriteLine("Hidden line.");
#line default
Console.WriteLine("Normal line #2.");


بعد از قرار دادن (BreakPoint) بر روی خطی که متن (“Hidden line”) را در خروجی درج می کند متوجه می شوید که با زدن (F10) هیچگاه به آن نمی رسید . خاصیت این (PD) همین است.




region# , #endregion :

فکر کنم با این (PD) بیشتر از بقیه آشنا هستید . همانطور که بهتر از من میدانید میتوانید با نوشتن (region#) در ابتدای کد هایتان و در انتها هم با نوشتن (endregion#) به کد های خود خوانایی بیشتری ببخشید و آن قسمت از کدها که با این (PD) مشخص کردید رآ تبدیل به یک بلوک از کد کنید و با (Expand) کردن بلوک را باز کنید و سپس بلوک را ببندید و بعد از بستن بلوک تنها تیتر آن مشاهده می شود .

// preprocessor_if.cs
#region MyClass definition
public class MyClass
{
public static void Main()
{
}
}
#endregion


حالا شاید پیش خودتون بگید که برای ساختن نسخه های دلخواه خودتون باید بالای هر فایلی که داخل اون از (PD) استفاده کردین مدام (define#) بنویسید و پاک کنید ?! ولی نه خیلی بهتر از این حرفاست :

روی Solution Configuration کلیک کنید .





بعد از باز شدن زیر منوها گزینه Configuration Manager رو انتخاب کنید .













همونطور که توی عکس نشون داده شده رو ComboBox زیر Active Solution Configuration کلیک کنید و از منوی باز شده گزینه New رو انتخاب کنید .



حالا کافیه در این قسمت نام هایی رو که فکر می کردین باید بالای هر فایلتون (define#) کنید رو اینجا بسازید تا بهتون اجازه بده به ازای هر اسمی که می سازید یک Setting جدا برای Compile شدن پروژه هاتون داشته باشید.

فقط یه نکته دیگه باقی میمونه و اون هم اینه که شما به (VS) بفهمونید که زمانیکه (Configuration) را ، شما روی هر حالت که قرار میدید کدام یک از (PD) هایی که شما در آن پروژه قرار داده اید فعال گردد برای این کار روی پروژه هایی که می خواید روی مد دلخواه شما Compile بشه چپ کلیک کنید و وارد Properties اون پروژه بشید :



سپس در گزینه (Conditional Compilation Symbols) نام (Solution Configuration) ی که نمونه اون رو در بالا ساختیم بنویسید به همین ترتیب برای پروژه های دیگه هم همین کار رو انجام بدید تا مشخص بشه هر پروژه بنا به (Active Solution Configuration) از چه تنظیماتی برای Compile شدن پیروی کند.