مقدمه

به فصل هفدهم و دنیای برنامه‌نویسی دینامیک خوش آمدید. تا به حال، ما همیشه در زمان کامپایل می‌دانستیم که با چه نوع داده‌ای کار می‌کنیم. اما چه می‌شود اگر بخواهیم در زمان اجرا، ساختار یک نوع داده را کشف کرده، اعضای آن را بررسی کنیم، یا حتی نمونه‌هایی از آن را بدون دانستن نام دقیقش در زمان کامپایل، ایجاد کنیم؟ این قدرت شگفت‌انگیز توسط مکانیزمی به نام انعکاس (Reflection) فراهم می‌شود. اما انعکاس بدون وجود یک مفهوم بنیادی‌تر ممکن نبود: فراداده (Metadata). در .NET، هر اسمبلی (assembly) نه تنها حاوی کد کامپایل‌شده است، بلکه شامل اطلاعات کاملی در مورد انواع داده‌ی تعریف‌شده در خود نیز می‌باشد. این فراداده، کلید اصلی خودتوصیف بودن اسمبلی‌ها و اساس کار ابزارهای قدرتمندی مانند IntelliSense و انعکاس است.

فراداده (Metadata) چیست؟

فراداده در ساده‌ترین تعریف، "داده درباره‌ی داده" است. در زمینه‌ی .NET، فراداده اطلاعاتی است که ساختار و ویژگی‌های انواع داده، اعضا و اسمبلی‌ها را توصیف می‌کند. وقتی شما یک پروژه‌ی C# را کامپایل می‌کنید، کامپایلر دو بخش اصلی را در فایل اسمبلی خروجی (.dll یا .exe) تولید می‌کند:

  1. کد CIL (Common Intermediate Language): نمایش سطح پایین و مستقل از پلتفرم کد شما.
  2. فراداده (Metadata): جداول باینری که تمام جزئیات مربوط به کد شما را توصیف می‌کنند.

این فراداده شامل اطلاعات بسیار دقیقی است، از جمله:

  • توصیف اسمبلی: نام، نسخه، فرهنگ و کلید عمومی اسمبلی (اطلاعاتی که در مانیفست اسمبلی دیدیم).
  • توصیف انواع داده: برای هر کلاس، ساختار، اینترفیس یا enum، اطلاعاتی مانند نام، سطح دسترسی (public, private و ...)، نام کلاس پایه و لیست اینترفیس‌های پیاده‌سازی شده.
  • توصیف اعضا: برای هر فیلد، متد، پراپرتی یا رویداد، اطلاعاتی مانند نام، نوع داده، امضای متد (پارامترها و نوع بازگشتی)، و هرگونه صفت (Attribute) سفارشی که به آن اعمال شده است.

به لطف وجود این فراداده‌ی غنی است که .NET می‌تواند قابلیت‌های پیشرفته‌ای را ارائه دهد.

نقش فراداده در اکوسیستم .NET

فراداده تنها برای استفاده‌ی داخلی CLR نیست؛ بلکه ستون فقرات بسیاری از ویژگی‌هایی است که ما روزانه با آن‌ها کار می‌کنیم.

۱. محیط‌های توسعه یکپارچه (IDEs) و IntelliSense

وقتی شما در ویژوال استودیو به یک کتابخانه ارجاع می‌دهید و پس از تایپ نام یک شیء و یک نقطه، لیستی از تمام متدها و پراپرتی‌های موجود به شما نمایش داده می‌شود، این قابلیت که IntelliSense نام دارد، مستقیماً در حال خواندن فراداده‌ی اسمبلی آن کتابخانه است. IDE کد شما را برای پیدا کردن این اطلاعات اجرا نمی‌کند؛ بلکه به سادگی جداول فراداده را خوانده و آن‌ها را به شکلی قابل فهم به شما نمایش می‌دهد. این فراداده همچنین به ابزارهایی مانند Object Browser در ویژوال استودیو قدرت می‌دهد تا ساختار کامل یک اسمبلی را به شما نشان دهند.

۲. Common Language Runtime (CLR)

CLR به شدت به فراداده برای مدیریت اجرای کد متکی است. برای مثال:

  • بارگذاری انواع داده: وقتی CLR برای اولین بار به یک نوع داده برخورد می‌کند، از فراداده برای پیدا کردن محل آن در حافظه، محاسبه‌ی اندازه‌ی آن و تخصیص حافظه‌ی لازم استفاده می‌کند.
  • امنیت نوع و صحت کد: در زمان کامپایل JIT (Just-In-Time)، CLR از فراداده برای تأیید اینکه کد CIL امن است و عملیات غیرمجازی (مانند دسترسی به حافظه‌ی خارج از محدوده) انجام نمی‌دهد، استفاده می‌کند.
  • فراخوانی متد: CLR از فراداده برای پیدا کردن آدرس متدها در حافظه و اجرای آن‌ها استفاده می‌کند.

۳. انعکاس (Reflection)

این مهم‌ترین کاربرد فراداده از دیدگاه برنامه‌نویس است. انعکاس APIای است که به ما اجازه می‌دهد تا در زمان اجرا، فراداده‌ی انواع داده را بخوانیم و با آن تعامل کنیم. با استفاده از انعکاس، ما می‌توانیم کدی بنویسیم که:

  • تمام انواع داده‌ی موجود در یک اسمبلی را لیست کند.
  • تمام متدها، پراپرتی‌ها و فیلدهای یک کلاس را کشف کند.
  • یک نمونه از یک کلاس را بدون دانستن نام آن در زمان کامپایل، ایجاد کند.
  • یک متد را به صورت دینامیک و با ارسال پارامترها در زمان اجرا، فراخوانی کند.

این قابلیت اساس کار بسیاری از فریم‌ورک‌های پیشرفته مانند فریم‌ورک‌های تست واحد، سریالایزرها، و سیستم‌های تزریق وابستگی (Dependency Injection) را تشکیل می‌دهد. در درس بعدی به طور کامل به بررسی API انعکاس خواهیم پرداخت.

فراداده در مقابل کد منبع

مهم است که بدانیم فراداده با کد منبع (source code) یکسان نیست. وقتی شما یک پروژه را کامپایل می‌کنید، کد منبع C# شما به کد CIL و فراداده تبدیل می‌شود. شما برای استفاده از یک کتابخانه، نیازی به کد منبع آن ندارید؛ فایل .dll که حاوی CIL و فراداده است، کافی است.

با این حال، ابزارهایی به نام decompiler (مانند ILSpy یا dotPeek) وجود دارند که می‌توانند از روی CIL و فراداده، کد منبع C# را با دقت بالایی بازسازی کنند. این نشان می‌دهد که فراداده‌ی ذخیره شده در اسمبلی‌ها چقدر غنی و کامل است.