مقدمه

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

System.Type: دروازه‌ی ورود به دنیای Reflection

نقطه‌ی شروع کار با انعکاس، کلاس System.Type است. یک شیء از نوع Type، نماینده‌ی فراداده‌ی یک نوع داده‌ی خاص (مانند یک کلاس، ساختار، یا اینترفیس) در زمان اجراست. هر نوع داده‌ای که در C# وجود دارد، یک شیء Type متناظر با خود دارد.

چگونه یک شیء Type به دست آوریم؟

چندین راه برای به دست آوردن شیء Type یک نوع وجود دارد:

  • استفاده از متد GetType(): همانطور که در درس مربوط به کلاس Object دیدیم، هر شیء در .NET متد GetType() را به ارث می‌برد. این متد، شیء Type مربوط به نوع واقعی آن شیء در زمان اجرا را برمی‌گرداند.
  • استفاده از عملگر typeof: اگر نام نوع را در زمان کامپایل می‌دانید، می‌توانید از عملگر typeof برای گرفتن شیء Type آن استفاده کنید.
  • استفاده از متد استاتیک Type.GetType(): این متد نام کامل نوع را به صورت یک رشته دریافت کرده و تلاش می‌کند تا شیء Type متناظر با آن را پیدا و برگرداند.
Copy Icon Program.cs
using System.Reflection;

// 1. Using GetType() on an instance
DateTime dt = DateTime.Now;
Type type1 = dt.GetType();
Console.WriteLine($"Type from GetType(): {type1.Name}");

// 2. Using the typeof operator (at compile time)
Type type2 = typeof(DateTime);
Console.WriteLine($"Type from typeof: {type2.Name}");

// 3. Using the static Type.GetType() method
// Note: You need the fully qualified name for types outside the core library.
Type type3 = Type.GetType("System.Int32");
Console.WriteLine($"Type from GetType(string): {type3.Name}");

بازرسی اعضای یک نوع

پس از اینکه یک شیء Type را به دست آوردیم، می‌توانیم از متدهای متعدد آن برای بازرسی فراداده‌ی اعضای آن نوع استفاده کنیم. این متدها معمولاً به صورت جفت هستند: یک متد برای گرفتن یک عضو خاص با نام مشخص، و یک متد برای گرفتن آرایه‌ای از تمام اعضای یک نوع خاص.

بیایید یک کلاس نمونه بسازیم و سپس با استفاده از انعکاس، اعضای آن را بازرسی کنیم.

Copy Icon Car.cs
public class Car
{
    public string Model { get; set; }
    private int _speed;

    public Car(string model) { Model = model; }
    
    public void Accelerate(int amount) { _speed += amount; }
}

حالا می‌توانیم فراداده‌ی این کلاس را بخوانیم:

Copy Icon Program.cs
Type carType = typeof(Car);
Console.WriteLine($"--- Inspecting {carType.Name} ---");

// Get all public methods
MethodInfo[] methods = carType.GetMethods();
Console.WriteLine("\nPublic Methods:");
foreach (MethodInfo method in methods)
{
    Console.WriteLine(method.Name);
}

// Get all public properties
PropertyInfo[] properties = carType.GetProperties();
Console.WriteLine("\nPublic Properties:");
foreach (PropertyInfo prop in properties)
{
    Console.WriteLine(prop.Name);
}

// Get all public constructors
ConstructorInfo[] constructors = carType.GetConstructors();
Console.WriteLine("\nPublic Constructors:");
foreach (ConstructorInfo ctor in constructors)
{
    Console.WriteLine(ctor.Name);
}

خروجی این کد، لیستی از تمام اعضای عمومی کلاس Car (به علاوه‌ی متدهایی که از System.Object به ارث برده) را نمایش می‌دهد.

دسترسی به اعضای غیرعمومی

متدهای GetMethods، GetProperties و ... به طور پیش‌فرض فقط اعضای public را برمی‌گردانند. برای دسترسی به اعضای private، protected یا داخلی، باید از یک نسخه‌ی سربارگذاری شده‌ی این متدها استفاده کنیم که یک BindingFlags را به عنوان پارامتر می‌پذیرد.

Copy Icon Program.cs
// To get non-public members, you need to provide BindingFlags.
FieldInfo privateField = carType.GetField("_speed", 
    BindingFlags.NonPublic | BindingFlags.Instance);

Console.WriteLine($"\nFound private field: {privateField.Name}");

هزینه‌ی عملکردی انعکاس

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