Sunday, December 09, 2007

Cکامپایلر

کامپايل يک برنامه C#

اجرای برنامه‌های C# از طريق کامپايلر C# دو نوع اطلاعات مهم ايجاد می‌کند : کد و metadata. در مطلب زير درباره اين دو نوع اطلاعات بحث خواهيم نمود.

زبان سطح ميانی مايکروسافت MSIL

کدی که توسط کامپايلر C# توليد مي‌شود به زبان خاصی است که به آن، زبان سطح ميانی مايکروسافت يا MSIL مي‌گويند. اين زبان مجموعه‌اي از دستورالعمل‌ها است که نحوه اجرای برنامه شما را معين مي‌کند. اين کد حاوی دستورالعمل‌هايي جهت مقداردهی متغيرها، فراخوانی اشياء، متدها و کنترل خطا مي‌باشد. بايد به اين نکته نيز اشاره کرد که زبان C# تنها زبانی نيست که کد آن به MSIL تبديل مي‌شود، بلکه کد تمامی زبانهای تحت .Net به زبان MSIL تبديل مي‌شوند، يعنی به محض کامپايل، کدهای نوشته شده توسط شما به کد زبان MSIL تبديل مي‌شود. به دليل اينکه کد تمامی زبانهای تحت .Net به دستورالعملهای مشابهی در MSIL تبديل مي‌شوند و به دليل اينکه تمامی اين زبانها دارای محيط اجرايي يکسانی هستند، از اينرو اين زبانها به راحتی مي‌توانند با يکديگر ارتباط برقرار نماييند و از کدهای يکديگر استفاده کنند و بدون مشکل با يکديگر کار کنند.

يکی از مزايای مهم MSIL اينست که دستورالعملهای آن مخصوص پردازنده‌ای (CPU) خاص طراحی نشده است. به بيان ديگر MSIL هيچ چيز در مورد CPU ماشينی که بر روی آن اجرا مي‌شود نمي‌داند و CPU ماشين نيز چيزی در مورد MSIL نمي‌داند. حال سوالی که در اينجا مطرح مي‌شود آنست که پس CPU چگونه کدهای MSIL را اجرا مي‌کند؟ بايد گفت که کدهای MSIL بر روی هر ماشينی، به دستورالعملهای خاص آن ماشين تبديل مي‌گردند. اين عمل، که تنها در اولين اجرای برنامه بر روی ماشينی خاص اتفاق می‌افتد را با نام "کامپايل آنی" يا JIT مي‌گوئيم. وظيفه کامپايلر JIT نيز تبديل کدهای MSIL به دستورالعمل‌های خاص ماشينی است که برنامه بر روی آن اجرا می شود.

همانطور که ملاحظه مي‌کنيد اين مرحله بسيار جالب است. اما شايد اين سوال در ذهن شما مطرح شود که، زمانيکه کامپايلر مي‌تواند کدی منطبق با دستورالعمل‌های پردازنده‌ای که روی آن اجرا مي‌شود توليد نمايد، پس چه لزومی به تبديل اين کدها به زبان MSIL وجود دارد؟ اين عمل دقيقا عملی است که در گذشته و توسط کامپايلرهای قديمی انجام می شد، اما دلايلی برای تبديل کدها به زبان MSIL وجود دارد. اولين و مهمترين دليل آنست که کدهای کامپايل شده به زبان MSIL را مي‌توان به سادگی به سخت‌افزار ديگری نيز منتقل نمود. برای مثال تصور کنيد برنامه‌ای را بر روی کامپيوتر شخصی خود به زبان C# نوشته و مي‌خواهيد آنرا به کامپيوتر جيبی خود منتقل نماييد. همانطور که مي‌دانيد اين دو نوع کامپيوتر دارای پردازنده‌هايي متفاوت هستند و دستورالعمل‌های هر يک از اين پردازنده‌ها نيز با يکديگر متفاوت است. از اينرو برنامه شما فقط بر روی کامپيوتر شخصی قابل اجرا خواهد بود و نمي‌توانيد آنرا بر روی سخت افزار ديگری منتقل کنيد. در اين شرايط شما به دو کامپايلر نياز خواهيد داشت : يک کامپايلر که برنامه C# شما را برای CPU کامپيوتر شخصی کامپايل کند و کامپايلر ديگری که اين برنامه را برای کامپيوتر جيبی کامپايل نمايد. همچنين در اين حالت بايد برنامه خود را دوبار کامپايل نماييد. اما با استفاده از MSIL تنها يکبار برنامه خود را کامپايل مي‌کنيد. در اين حالت با نصب .Net Framework و JIT بر روی کامپيوتر شخصی خود، برنامه‌تان اجرا مي‌شود و با نصب آن بر روی کامپيوتر شخصی، برنامه شما اجرا مي‌شود. حال شما کد MSIL ای داريد که بر روی هر ماشينی که دارای کامپايلر JIT مربوط به .Net Framework است، اجرا می شود.

يکی ديگر از مزايای استفاده از زبان MSIL در آنست که کدهای توليد شده در اين مرحله به سادگی در پروسه تاييد کد (Code Verification) قابل خواندن هستند. پروسه تاييد کد، پروسه‌اي است که در آن JIT کد توليد شده را بررسی مي‌کند تا فاقد هرگونه خطا و مشکل باشد. از جمله بررسی‌هايي که در اين مرحله انجام مي‌شود عبارتند از : بررسی دسترسی صحيح به حافظه، استفاده از متغير صحيح، تبديل صحيح انواع مختلف به يکديگر و .... اين بررسی ها باعث مي‌شوند تا برنامه‌ها بهنگام اجرا دچار مشکل نشده و با موفقيت اجرا شوند. توليد کدهای مختص به يک نوع پردازنده، هرچند سرعت اجرا را با بالا مي‌برد اما کدی توليد مي‌کند که بررسی آن بسيار دشوار است. تبديل کدهای نوشته شده به زبان C# به کدی مختص پردازنده‌ای خاص باعث ايجاد کدی مي‌گردد که در مرحله تاييد کد غير قابل تشخيص است و يا به سختی قابل بررسی است.

Metadata

پروسه کامپايل، علاوه بر کد اصلی برنامه، حاوی خروجی ديگری با عنوان Metadata می باشد که بخش بسيار مهمی در به اشتراک‌گذاری کدهای .Net مي‌باشد. چه بخواهيد تا برنامه‌ای مستقل با C# توليد کنيد و چه بخواهيد کلاسی توليد کنيد که در برنامه‌ای ديگر مورد استفاده قرار گيرد، ممکن است بخواهيد تا از کدهايي که قبلاً کامپايل شده‌اند استفاده نماييد. اين کد يا توسط مايکروسافت بعنوان بخشی از .Net Framework پشتيبانی مي‌شوند و يا توسط شخصی ديگر منطبق با .Net توليد شده‌اند. زمانيکه بخواهيد از اين کد استفاده کنيد، کامپايلر C# شما بايد اطلاعاتی درباره متغيرها و نوع متغيرهای بکار رفته در اين کد در اختيار داشته باشد تا بتواند خود را با آن منطبق نمايد.

Metadata را مي‌توان بعنوان فهرست اطلاعات موجود در کدی که قبلاً کامپايل شده در نظر گرفت. C# فايل Metadata را در هنگام توليد کد MSIL توليد مي‌کند و در آن اطلاعات مفيدی را قرار مي‌دهد. اين فايل حاوی اطلاعاتی در مورد کليه کلاسهای کد توليد شده و ساختار آنها مي‌باشد. همچنين در Metadata تمامی متدها و متغيرهای بکار رفته در کلاس بطور کامل توصيف شده و برای برنامه‌های ديگر قابل خواندن است. بعنوان مثال يک برنامه مي‌تواند با استفاده از Metadata موجود، ليست کليه متدهای موجود را استخراج کرده و مورد استفاده قرار گيرد. تمامی اطلاعات لازم جهت توصيف و بررسی يک کلاس در Metadata قرار مي‌گيرد.

اسمبلي‌ها (Assemblies)

در اکثر موارد، شما از زبان C# بعنوان زبانی برای توليد برنامه‌های کاربردی استفاده مي‌کنيد. اين برنامه‌های کاربردی بصورت فايلهايي اجرايي در می‌آيند که دارای پسوند .EXE هستند. ويندوز همواره فايلهای اجرايي را با پسوند .EXE مي‌شناسد و C# نيز توليد اينگونه فايلها را کاملاً پشتيبانی مي‌کند.

اما زمانهايي هستند که نيازی به توليد کل يک برنامه نيست و تنها لازم است تا کلاس و يا ابزاری برای يک برنامه جامع‌تر توليد شود. برای مثال فرض کنيد مي‌خواهيد ابزاری با زبان C# توليد کنيد و آنرا به گروهی بدهيد که با VB.Net کار مي‌کنند. در چنين شرايطی بجای توليد فايل اجرايي .EXE، فايل اسمبلی توليد مي‌شود.

اسمبلی بسته‌ای حاوی کد و Metadata است. هنگاميکه مجموعه‌ای از کلاسها را در يک اسمبلی جمع مي‌کنيد، تمامی اين کلاسها بعنوان يک واحد در نظر گرفته مي‌شوند و دارای سطوح يکسانی هستند. مي‌توان به اسمبلی بعنوان يک DLL منطقی (Logical DLL) نگريست. در .Net بحث اسمبلی بسيار شبيه به Microsoft Transaction Server و COM+ است.

بطور کلی دو نوع اسمبلی وجود دارد : اسمبلی‌هاي خصوصی و اسمبلی‌های عمومی. در هنگام توليد اسمبلی نيازی به مشخص کردن نوع اسمبلی نيست. اسمبلی‌های خصوصی تنها برای يک برنامه قابل دسترس هستند. در اين حالت اسمبلی شما بصورت يک DLL ايجاد مي‌شود و در دايرکتوری مشابه با برنامه‌ای که از آن استفاده مي‌کند، نصب مي‌گردد. با توليد اسمبلی خصوصی (Private) تنهای برنامه‌ای که مي‌تواند از اسمبلی شما استفاده کند، برنامه اجرايي است که در دايرکتوری مشابه با اسمبلی شما قرار داشته باشد.

اما اگر بخواهيد برنامه‌های متعددی از کد شما استفاده کنند، از اسمبلی عمومی (Global) استفاده مي‌کنيد. اسمبلي‌های عمومی توسط کليه برنامه‌هاي .Net موجود بر روی سيستم قابل استفاده هستند. مايکروسافت اين اسمبلي‌ها را بعنوان اسمبلي‌های .Net در نظر مي‌گيرد و کليه اسمبلي‌هاي .Net برای کليه برنامه‌ها قابل استفاده هستند. .Net Framework شامل ليستی از اسمبلي‌های عمومی است که تحت عنوان global assembly cache شناخته مي‌شوند و .Net Framework SDK شامل ابزارهايي جهت نصب و پاک کردن اسمبلي‌های مختلف از اين دايرکتوری مي‌باشد.

کامپايل برنامه در Command Line

پس از اينکه برنامه خود را نوشتيد، نوبت به کامپايل آن مي‌رسد. توجه کنيد که برای کامپايل برنامه‌های C# حتماً به محيط ويژوال نيازی نيست. چون دوستان بسياری به من ايميل زدند و درخواست کرده‌اند تا نحوه کامپايل برنامه‌ها را در Command Line را توضيح دهم، در اينجا به توضيح درباره آن پرداختم. توجه کنيد که شما برای شروع کار حتی به محيطی مثل VS.Net و يا C#Builder هم نيازی نداريد. تنها کافيست تا کد برنامه خود را در يک ويرايشگر متن مانند Notepad تايپ کنيد و سپس از طريق Command Line به اجرای آن بپردازيد. تنها نکته‌ای که بايد به آن توجه نماييد آنست که در هنگام ذخيره کردن کدی که در Notepad نوشته ايد، بايد به انتهای نام فايل خود پسوند .cs را اضافه نماييد.

حال نوبت به کامپايل کد نوشته شده مي‌رسد. کامپايلر Command Line بطور پيش فرض در دايرکتوری زير وجود دارد : c:\windows\Microsoft.Net\Framework\v.xxxx تحت عنوان csc.exe قرار دارد که xxxx شماره ورژن .Net Framework نصب شده بر روی سيستم شماست. فرض کنيد مي‌خواهيم برنامه‌ای با نام Hello.cs را که در محيط Notepad آنرا نوشته‌ايم را کامپايل کنيم. وارد Command Line شده و عبارت csc Hello.cs را تايپ کنيد. پس از اين دستو در صورتيکه کد شما دارای خطا نباشد، در همان دايرکتوری موجود فايل اجرايي تحت عنوان Hello.exe توليد خواهد شد که همان برنامه کامپايل شده شماست.

نکته مهمی که بايد به آن توجه کنيد، وجود کامپايلر در مسيری است که فايل کد مورد نظر شما قرار دارد. برای مثال در صورتيکه csc.exe را به مسير ويندوز اضافه نکرده باشيد، برای اجرای مثال قبل بايد فايل نوشته شده را در مسير csc.exe قرار دهيد و سپس از طريق Command Line آنرا اجرا نماييد. برای مثال اجرای اين برنامه بر روی کامپوتر خود من بشکل زير است. (علت اينكه گفته‌ام بر روي كامپيوتر خود من، بخاطر متفاوت بودن مسيرهاي نصب برنامه بر روي كامپيوتر هاي مختلف است.!)



همانطور که مشاهده مي‌کنيد، پس از اجرای دستور گفته شده، در صورتيکه برنامه بدون خطا باشد، پيغام خطايي ظاهر نشده و فايل اجرايي برنامه توليد مي‌شود.

کدی که من برای Hello.cs در نظر گرفته بودم بصورت زير است :

class HelloWorld

{

public static void Main()

{

System.Console.WriteLine("Hello World!");

System.Console.ReadLine();

}

}

با اجرای کد فوق فايل اجرايي آن ايجاد شده و قابل استفاده مي‌گردد. در صورتيکه کد شما دارای خطا باشد، پس از کامپايل کد از طريق Command Line ، خطی از کد که خطا در آن وجود دارد با نوع خطای آن برای شما نمايش داده مي‌شود.

class HelloWorld

{

public static void Main()

{

System.Console.WriteLine("Hello World!");

System.Console.ReadLine()

}

}

برای مثال در کد فوق، در خط آخر سمی کالون ";" را حذف کرده‌ام تا خطايي اتفاق افتد. خروجی کامپايلر بصورت شکل زير در خواهد آمد.



همانطور که در شکل بالا مشاهده مي‌کنيد، پس از اجرای کامپايلر C#، خطايي ظاهر مي‌شود که در آن نشان مي‌دهد که برنامه در سطر 6 و ستون 26 دارای خطا است که اين خطا عدم وجود ";" است.

كار با كامپايلر Command Line اندكي دشوار بوده و نياز به تمرين و اندكي تامل دارد.