مقدمه
در درس گذشته، با دایرکتیوها و صفتهای سطح بالای CIL که ساختار کلی یک اسمبلی و انواع
دادهی درون آن را تعریف میکنند، آشنا شدیم. دیدیم که چگونه دایرکتیو .class یک نوع جدید را معرفی میکند و .assembly اطلاعات کلی اسمبلی را مشخص مینماید. اکنون زمان آن رسیده که
به درون این ساختارها نفوذ کرده و ببینیم که اعضای مختلف یک نوع (type members) - یعنی فیلدها،
متدها، سازندهها و پراپرتیها - چگونه در سطح زبان میانی مشترک تعریف و توصیف میشوند.
هر عضوی که ما در C# تعریف میکنیم، یک نمایش متناظر و دقیق در CIL دارد.
کامپایلر C# وظیفه دارد تا ساختارهای سطح بالای زبان را به این تعاریف سطح پایینتر
ترجمه کند. درک این فرآیند ترجمه به ما کمک میکند تا بفهمیم ویژگیهایی مانند پراپرتیها یا
سازندهها که در C# به نظر یکپارچه میآیند، در واقع چگونه از ترکیب چندین جزء در
CIL ساخته شدهاند.
تعریف فیلدها با دایرکتیو .field
فیلدها (Fields)، که متغیرهای عضو یک کلاس یا ساختار هستند، با استفاده از دایرکتیو .field در CIL تعریف میشوند. این دایرکتیو نه تنها نام و
نوع فیلد را مشخص میکند، بلکه ویژگیهای دیگری مانند سطح دسترسی و استاتیک بودن را نیز تعیین
مینماید.
سینتکس کلی به این صورت است: .field [attributes] type fieldName
بیایید یک کلاس C# با چند فیلد متفاوت را در نظر بگیریم و ببینیم کامپایلر چگونه آنها
را به CIL ترجمه میکند.
C# Code
public class Vehicle
{
private int _horsePower;
public static readonly int NumberOfWheels = 4;
}
کد CIL تولید شده برای این فیلدها به شکل زیر خواهد بود:
CIL Code
.class public auto ansi beforefieldinit Vehicle extends [System.Runtime]System.Object
{
.field private int32 _horsePower
.field public static literal int32 NumberOfWheels = int32(4)
}
بیایید این کد را تحلیل کنیم:
- .field private int32 _horsePower: این خط یک فیلد نمونه
(instance) به نام _horsePower تعریف میکند. صفت private سطح دسترسی آن را مشخص
میکند و int32 نوع دادهی آن است که معادل int در C# میباشد.
- .field public static literal int32 NumberOfWheels...: این خط یک
فیلد استاتیک و عمومی را تعریف میکند. کلمهی کلیدی static نشان میدهد که این فیلد به
خود کلاس Vehicle تعلق دارد، نه به یک نمونهی خاص. اما نکتهی جالب، کلمهی کلیدی
literal است. کامپایلر C#، فیلدهای const و فیلدهای static readonly که
با یک مقدار ثابت در زمان کامپایل مقداردهی شدهاند را به عنوان یک "لیترال" در فراداده ثبت
میکند. این به CLR اجازه میدهد تا این مقدار را بهینه کرده و مستقیماً در کد جایگزین کند. اگر
`NumberOfWheels` در سازندهی استاتیک مقداردهی میشد، به جای literal از صفت
initonly (معادل readonly) استفاده میشد.
تعریف متدها با دایرکتیو .method
همانطور که در درسهای قبل دیدیم، متدها با دایرکتیو .method تعریف
میشوند. این دایرکتیو امضای کامل متد را مشخص میکند.
صفتهای متداول متدها
علاوه بر سطح دسترسی (public, private, family که معادل protected است) و ماهیت (static,
virtual, abstract)، دو صفت ویژه وجود دارند که اغلب در CIL تولید شده توسط کامپایلر
دیده میشوند:
- specialname: این صفت نشان میدهد که نام این متد برای کامپایلرها
یا ابزارها معنای خاصی دارد. این صفت معمولاً برای متدهایی که مستقیماً در کد C#
قابل فراخوانی نیستند، مانند سازندهها و اکسسورهای پراپرتیها، به کار میرود.
- rtspecialname: این صفت که همیشه با specialname همراه است،
نشان میدهد که خود CLR نیز با این متد به صورت ویژهای رفتار میکند. این صفت تقریباً همیشه
برای سازندهها (.ctor) به کار میرود.
نمایش سازندهها و پراپرتیها
سازندهها و پراپرتیها در C#، مفاهیم سطح بالایی هستند که در CIL به ترکیبی
از متدها و دایرکتیوهای دیگر ترجمه میشوند.
سازندهها به عنوان .ctor
هر سازندهای که در C# مینویسید، به یک متد با نام ثابت .ctor (مخفف constructor) در CIL ترجمه میشود. این متدها
همیشه با صفتهای specialname و rtspecialname علامتگذاری میشوند.
CIL for a Constructor
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
}
پراپرتیها: ترکیبی از .property و متدهای get/set
یک پراپرتی در C#، یک مفهوم انتزاعی است. کامپایلر آن را به سه بخش در CIL
ترجمه میکند:
- یک فیلد پشتیبان (backing field) خصوصی (اگر پراپرتی خودکار باشد).
- دو متد اکسسور: یکی برای get (با پیشوند get_) و یکی برای set (با
پیشوند set_). این متدها با صفت specialname علامتگذاری میشوند.
- یک دایرکتیو .property که این دو متد را به یک نام پراپرتی واحد متصل
میکند.
بیایید پراپرتی public string Name { get; set; } را در نظر بگیریم. کد CIL آن (به
صورت سادهشده) به شکل زیر خواهد بود:
CIL for a Property
.field private string '<Name>k__BackingField'
.property instance string Name()
{
.get instance string MyNamespace.MyClass::get_Name()
.set instance void MyNamespace.MyClass::set_Name(string)
}
.method public hidebysig specialname instance string
get_Name() cil managed
{
}
.method public hidebysig specialname instance void
set_Name(string 'value') cil managed
{
}
همانطور که میبینید، یک پراپرتی ساده در C# به یک ساختار نسبتاً پیچیده در
CIL تبدیل میشود. این دایرکتیو .property است که به ابزارهایی مانند ویژوال استودیو و
دیباگر میگوید که این دو متد در واقع اکسسورهای یک پراپرتی واحد هستند و باید به صورت یکپارچه
نمایش داده شوند.