مقدمه
به فصل دوازدهم خوش آمدید. در این فصل با یکی از قدرتمندترین و بنیادیترین مفاهیم .NET
یعنی نمایندگان (Delegates) آشنا میشویم. اگر با زبانهایی مانند C/C++ آشنا
باشید، احتمالاً با مفهوم "اشارهگر به تابع" (function pointer) برخورد داشتهاید. یک نماینده در
C#، معادل مدرن، شیءگرا و امن همان مفهوم است. یک نماینده، یک نوع دادهی ارجاعی ویژه
است که به جای نگهداری داده، یک ارجاع به یک یا چند متد را در خود نگه میدارد.
این قابلیت به ما اجازه میدهد تا متدها را مانند متغیرها به اطراف پاس دهیم، آنها را به عنوان
پارامتر به متدهای دیگر ارسال کنیم و کدهایی بسیار انعطافپذیر و پویا بنویسیم. نمایندگان، اساس و
پایهی برنامهنویسی رویدادمحور (event-driven) و عبارات لامبدا در C# هستند.
نماینده (Delegate) چیست؟
یک نماینده را میتوان به عنوان یک "قرارداد" یا "امضا" برای یک متد در نظر گرفت. شما یک نوع
نماینده تعریف میکنید که مشخص میکند چه نوع متدهایی (با چه پارامترهای ورودی و چه نوع خروجی)
میتوانند به آن اختصاص داده شوند. پس از آن، میتوانید هر متدی را که با آن امضا مطابقت داشته
باشد، درون یک متغیر از آن نوع نماینده قرار دهید و سپس آن متد را از طریق آن متغیر فراخوانی کنید.
تعریف و استفاده از یک نماینده
کار با نمایندگان شامل سه مرحلهی اصلی است: تعریف نوع نماینده، ساخت نمونه و تخصیص متد، و در نهایت
فراخوانی نماینده.
قدم اول: تعریف نوع نماینده
ابتدا باید با استفاده از کلمهی کلیدی delegate، امضای متدهایی که
میخواهیم به آنها ارجاع دهیم را تعریف کنیم.
Program.cs
public delegate int BinaryOperation(int x, int y);
در اینجا، ما یک نوع جدید به نام BinaryOperation تعریف کردهایم.
قدم دوم و سوم: ساخت نمونه و فراخوانی
اکنون میتوانیم متدهایی بسازیم که با این امضا مطابقت دارند و یک نمونه از نمایندهی خود را برای
ارجاع به آنها ایجاد کنیم.
Program.cs
public class Calculator
{
public static int Add(int a, int b) => a + b;
public static int Subtract(int a, int b) => a - b;
}
BinaryOperation op = Calculator.Add;
int result1 = op(10, 5);
Console.WriteLine(result1);
op = Calculator.Subtract;
int result2 = op(10, 5);
Console.WriteLine(result2);
نمایندههای چندپخشی (Multicast Delegates)
یکی از ویژگیهای بسیار قدرتمند نمایندگان این است که میتوانند به بیش از یک متد
به طور همزمان ارجاع دهند. به این نوع نماینده، نمایندهی چندپخشی گفته میشود. شما میتوانید با
استفاده از عملگرهای + یا += متدهای جدیدی را به لیست فراخوانی
یک نماینده اضافه کنید و با -
یا -= آنها را حذف کنید.
Program.cs
public delegate void ProgressReporter(int percentComplete);
public class Util
{
public static void ReportToConsole(int percent) { Console.WriteLine($"Progress: {percent}%"); }
public static void ReportToFile(int percent) { System.IO.File.AppendAllText("progress.log", $"Progress: {percent}%\n"); }
}
ProgressReporter reporter = Util.ReportToConsole;
reporter += Util.ReportToFile;
reporter(50);
reporter -= Util.ReportToConsole;
reporter(100);
وقتی reporter(50) فراخوانی میشود، ابتدا متد ReportToConsole
و سپس ReportToFile اجرا
میگردد. این قابلیت در برنامهنویسی رویدادمحور که در آن چندین "شنونده" ممکن است بخواهند به یک
رویداد واکنش نشان دهند، بسیار کاربردی است.
نماینده به عنوان پارامتر متد
یکی از رایجترین کاربردهای نمایندگان، ارسال آنها به عنوان پارامتر به متدهای دیگر است. این کار
به شما اجازه میدهد تا بخشی از منطق یک متد را به صورت یک "پلاگین" یا "callback" از بیرون به آن
تزریق کنید.
public static void ProcessNumbers(List<int> numbers, BinaryOperation operation)
{
foreach (var num in numbers)
{
Console.WriteLine(operation(num, 10));
}
}
List<int> myNumbers = new() { 5, 8, 12 };
Console.WriteLine("--- Adding ---");
ProcessNumbers(myNumbers, Calculator.Add);
Console.WriteLine("\n--- Subtracting ---");
ProcessNumbers(myNumbers, Calculator.Subtract);
در این الگو، متد ProcessNumbers یک منطق کلی (پیمایش لیست) را پیادهسازی میکند، اما
عملیات
مشخصی که روی هر عنصر انجام میشود (Add یا Subtract) توسط کد فراخواننده تعیین
میگردد. این
روش کد را بسیار انعطافپذیر و قابل استفاده مجدد میکند.