مقدمه
در درس گذشته با گرامر پایهای زبان میانی مشترک (CIL) و دستورات مبتنی بر پشتهی آن
آشنا شدیم. دیدیم که چگونه دستورات سادهای مانند ldarg، add و ret برای اجرای
منطق یک متد به کار میروند. اما یک اسمبلی .NET بسیار بیشتر از مجموعهای از
دستورالعملهای اجرایی است؛ این یک ساختار پیچیده با فرادادهی غنی است که انواع داده، اعضای آنها،
وابستگیها و اطلاعات نسخهبندی را توصیف میکند. این ساختار و فراداده چگونه در CIL
تعریف میشوند؟ پاسخ در دایرکتیوها (Directives) و صفتها
(Attributes) نهفته است.
دایرکتیوها که با یک نقطه شروع میشوند، به اسمبلر (ilasm.exe)
و CLR دستور میدهند که چگونه ساختار اسمبلی را بسازند. آنها معادل کلمات کلیدی ساختاری در
C# مانند class، namespace و public هستند. صفتها در
CIL نیز، مانند صفتها در C#، فرادادهی اضافی را به عناصر مختلف متصل
میکنند. در این درس به تفصیل این دو مفهوم را بررسی میکنیم.
دایرکتیوهای CIL: تعریف ساختار
دایرکتیوها اسکلت یک اسمبلی را تشکیل میدهند. آنها همه چیز را از تعریف خود اسمبلی گرفته تا
کلاسها، متدها و فیلدهای درون آن مشخص میکنند. بیایید برخی از مهمترین دایرکتیوها را بررسی کنیم.
دایرکتیوهای سطح اسمبلی
این دایرکتیوها اطلاعات کلی در مورد کل اسمبلی را تعریف میکنند و معمولاً در ابتدای فایل .il قرار میگیرند.
- .assembly extern <Name>: این دایرکتیو یک ارجاع به یک
اسمبلی خارجی را تعریف میکند. هر اسمبلی .NET حداقل به کتابخانهی هستهی
.NET (که در نسخههای قدیمیتر mscorlib و در نسخههای مدرن System.Runtime
نامیده میشود) نیاز دارد. این دایرکتیو به CLR میگوید که انواع دادهی پایهای مانند
System.Object یا System.Int32 در کجا قرار دارند.
- .assembly <Name>: نام اسمبلی فعلی را مشخص میکند. این
دایرکتیو همچنین میتواند شامل دایرکتیوهای تودرتو برای تعیین نسخه (.ver) یا کلید عمومی
(.publickeytoken) باشد.
- .module <FileName>: نام فایل فیزیکی ماژول را مشخص میکند
(معمولاً همان نام فایل .dll یا .exe).
Assembly Directives Example
.assembly extern System.Runtime { }
.assembly MyAwesomeLibrary
{
.ver 1:0:0:0
}
.module MyAwesomeLibrary.dll
کد بالا یک اسمبلی به نام MyAwesomeLibrary با نسخهی 1.0.0.0 را تعریف میکند که به اسمبلی
System.Runtime وابستگی دارد.
دایرکتیوهای سطح نوع (Type-Level)
مهمترین دایرکتیو در این سطح، .class است که برای تعریف یک کلاس،
اینترفیس یا ساختار به کار میرود. این دایرکتیو میتواند با چندین صفت همراه شود تا ویژگیهای نوع
را مشخص کند:
- سطح دسترسی: public یا private (که در CIL به آن
assembly گفته میشود).
- وراثت: کلمهی کلیدی extends برای مشخص کردن کلاس پایه به کار میرود.
کلمهی کلیدی implements برای پیادهسازی اینترفیسها استفاده میشود.
- دیگر صفتها: abstract (برای کلاسهای انتزاعی)، sealed (برای
کلاسهای مهر و موم شده)، auto (برای چیدمان خودکار فیلدها توسط CLR) و ansi
(برای نحوهی مارشال کردن رشتهها).
Class Definition in CIL
.class public auto ansi sealed beforefieldinit MySealedClass
extends [System.Runtime]System.Object
implements [MyContracts]MyContracts.ISomeInterface
کد بالا یک کلاس public sealed به نام MySealedClass را تعریف میکند که از
System.Object ارثبری کرده و اینترفیس ISomeInterface را پیادهسازی میکند.
صفتهای CIL
همانطور که در C# از صفتها برای افزودن فرادادهی اضافی استفاده میکنیم، در
CIL نیز میتوانیم این کار را انجام دهیم. صفتها در CIL با استفاده از
دایرکتیو .custom تعریف میشوند.
فرض کنید در C# صفت [Serializable] را به یک کلاس اعمال کردهایم:
C# Code
[Serializable]
public class Person { }
کامپایلر این کد را به CIL زیر ترجمه میکند:
CIL Code
.class public auto ansi serializable beforefieldinit Person
extends [System.Runtime]System.Object
{
.custom instance void [System.Runtime.Serialization]System.SerializableAttribute::.ctor() = ( 01 00 00 00 )
// ... class members ...
}
در اینجا، دایرکتیو .custom به کامپایلر میگوید که یک نمونه از کلاس
System.SerializableAttribute را ایجاد کرده و آن را به کلاس Person متصل کند. بخش ::.ctor()
به سازندهی پیشفرض آن صفت اشاره دارد. این مکانیزم به ما اجازه میدهد تا هر نوع فرادادهی سفارشی
را به عناصر کد خود در سطح پایین متصل کنیم.