آموزش استفاده از delegate در سی شارپ
delegate یک نوعِ داده(date type) است که به ما امکان می دهد تا ارجاع به توابع را ذخیره کنیم. اگرچه این عبارت بسیار پیچیده به نظر میرسد، اما مکانیسم آن بسیار ساده است. مهمترین هدف استفاده از delegate بعداً در این کتاب، وقتی که رویدادها و event handling را بررسی می کنیم، مشخص خواهد شد. اما خوب است به طور خلاصه آن را کمی در اینجا توضیح دهیم.
Delegate ها، بیشتر شبیه تعریف کردن تابع ها هستند. اما مثل تابع ها، بدنه ندارند و برای ایجاد، از کلمه ی کلیدی delegate استفاده می کنند. در تعریف یک delegate یک نوعِ برگشتی و تعدادی پارامتر مشخص می شود. پس از تعریف یک delegate، می توانیم یک متغیر از نوع آن delegate تعریف کنیم. سپس می توانیم این متغیر را با استفاده از کلمه ی کلیدی new، برای رجوع به هر تابعی که نوع برگشتی و پارامترهای یکسانی دارد، مقداردهی اولیه(initialize) کنیم. وقتی که این کار را انجام دادیم، می توانیم آن تابع را با استفاده از این متغیر، فراخوانی کنیم.
وقتی که یک متغیر داریم که به یک تابع رجوع می کند، می توانیم عملیات دیگری را نیز انجام دهیم. بعنوان مثال، می توانیم یک متغیر delegate را به یک تابع، بعنوان یک پارامتر پاس دهیم؛ سپس این تابع می تواند از این delegate برای فراخوانی هر تابعی که به آن اشاره دارد، استفاده کند. بدون اینکه بدانیم کدام تابع تا هنگام اجرا، فراخوانی خواهد شد. در مثال خودتان امتحان کنید زیر، از یک delegate برای دسترسی به یکی از دو تابع استفاده شده است.
خودتان امتحان کنید: استفاده از یک Delegate برای فراخوانی یک تابع: Ch06Ex05\Program.cs
1. یک اپلیکیشن کنسول به نام Ch06Ex05 ایجاد کنید و آن را در آدرس C:\BeginningCSharp7\Chapter06 ذخیره کنید.
2. کد زیر را در فایل Program.cs اضافه کنید:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
using static System.Convert;
namespace Ch06Ex05
{
class Program
{
delegate double ProcessDelegate(double param1, double param2);
static double Multiply(double param1, double param2) => param1 * param2;
static double Divide(double param1, double param2) => param1 / param2;
static void Main(string[] args)
{
ProcessDelegate process;
WriteLine("Enter 2 numbers separated with a comma:");
string input = ReadLine();
int commaPos = input.IndexOf(',');
double param1 = ToDouble(input.Substring(0, commaPos));
double param2 = ToDouble(input.Substring(commaPos + 1,
input.Length - commaPos - 1));
WriteLine("Enter M to multiply or D to divide:");
input = ReadLine();
if (input == "M")
process = new ProcessDelegate(Multiply);
else
process = new ProcessDelegate(Divide);
WriteLine($"Result: {process(param1, param2)}");
ReadKey();
}
}
}
3. کد را اجرا کنید و وقتی از شما مقادیری درخواست شد، آن مقادیر را وارد کنید. تصویر 6.12 نتیجه را نشان می دهد.
(تصویر 6.12)

در این کد، یک delegate به نام ProcessDelegate تعریف کرده ایم که نوع برگشتی(return type) و پارامترهای آن با دو تابع Multiply() و Divide() مطابقت دارد. توجه کنید که تابع های Multiply() و Divide() از علامت لامبدا، یعنی علامت => استفاده می کنند.
static double Multiply(double param1, double param2) => param1 * param2;
تعریف این delegate به صورت زیر است:
delegate double ProcessDelegate(double param1, double param2);
کلمه ی کلیدی delegate مشخص می کند که این تعریف یک delegate است نه یک تابع. این تعریف، در همان جایی ظاهر میشود که تعریف یک تابع ممکن است قرار گیرد. سپس این تعریف یک مقدار برگشتی از نوع double و دو پارامتر نیز از نوع double مشخص می کند. نام های به کار رفته، اختیاری هستند؛ ما می توانیم هرجایی که دوست داشته باشیم، نوع و پارامترهای delegate را فراخوانی کنیم.
این مثال از یک delegate به نام ProcessDelegate و پارامترهایی از نوع double به نام param1 و param2 استفاده می کند. در زیر، کد به کار رفته در Main() با تعریف یک متغیر، با استفاده از نوعِ این delegate جدید، شروع می شود:
static void Main(string[] args)
{
ProcessDelegate process;
سپس، مقداری کد نسبتاً استاندارد سی شارپ را داریم که دو عدد که با استفاده از کاما از یکدیگر جدا شده اند را درخواست می کند. سپس این اعداد را در دو متغیر double قرار می دهد:
WriteLine("Enter 2 numbers separated with a comma:");
string input = ReadLine();
int commaPos = input.IndexOf(',');
double param1 = ToDouble(input.Substring(0, commaPos));
double param2 = ToDouble(input.Substring(commaPos + 1,input.Length - commaPos - 1));
نکته: برای اهداف نمایشی، در اینجا از اعتبارسنجی ورودی ها(input) استفاده نکرده ایم. اگر این یک کد واقعی بود، باید زمان زیادی را صرف می کردیم تا معلوم شود که مقادیر معتبری را در متغیرهای محلی param1 و param2 قرار داده ایم.
سپس، از کاربر می خواهیم تا این اعداد را در یکدیگر ضرب یا تقسیم کند.
WriteLine("Enter M to multiply or D to divide:");
input = ReadLine();
بسته به انتخاب کاربر، ما متغیر دلیگیت process را مقداردهی اولیه می کنیم:
if (input == "M")
process = new ProcessDelegate(Multiply);
else
process = new ProcessDelegate(Divide);
برای انتساب یک ارجاع تابع(a function reference) به یک متغیر delegate، از یک سینتکس یا شیوه ی نوشتاری عجیب استفاده می کنیم. بسیار شبیه به انتساب مقادیر آرایه ها، می توانیم از کلمه ی کلیدی new برای ایجاد یک delegate جدید، استفاده کنیم. پس از این کلمه ی کلیدی، نوعِ delegate را مشخص می کنیم، و یک آرگومان که به تابعی که می خواهیم از آن استفاده کنیم، اشاره می کند را ارائه می دهیم، برای مثال تابع Multiply() یا Divide(). این آرگومان تطابقی با پارامترهای نوعِ delegate یا تابع هدف ندارد. این یک سینتکس یکتا برای انتساب delegate است.
این آرگومان، نام تابعی است که می خواهیم استفاده کنیم، بدون هیچ پرانتزی. در حقیقت، اگر بخواهیم می توانیم در اینجا از یک سینتکس ساده تر استفاده کنیم:
if (input == "M")
process = Multiply;
else
process = Divide;
کامپایلر تشخیص می دهد که نوع دلیگیتِ متغیرِ process، با نام و نوع هریک از پارامترهای(signature) دو تابع تطابق دارد و به طور اتوماتیک یک delegate را برای ما مقداردهی اولیه می کند. اینکه از چه سینتکسی استفاده کنید، به شما بستگی دارد، با اینکه برخی ترجیح می دهند که از نسخه ی معمولی استفاده کنند. زیرا با یک نگاه میتوان راحتتر دید که چه اتفاقی میافتد. در نهایت، تابع انتخاب شده را با استفاده از این delegate فراخوانی کنید. همان سینتکس، صرفنظر از اینکه، delegate به کدام تابع اشاره می کند، کار می کند:
WriteLine($"Result: {process(param1, param2)}");
ReadKey();
}
در اینجا،گویی ما این متغیر delegate به صورت یک نام تابع تلقی می کنیم. برخلاف یک تابع، همچنین می توانیم روی این متغیر عملیات اضافی، مانند پاس دادنِ آن به یک تابع از طریق یک پارامتر نیز انجام دهیم؛ همان طور که در مثال ساده ی زیر نشان داده شده است:
static void ExecuteFunction(ProcessDelegate process)
=> process(2.2, 3.3);
این یعنی اینکه ما می توانیم رفتار تابع ها را با پاس دادن delegate های تابع به آنها، کنترل کنیم. بعنوان مثال، ممکن است یک تابع داشته باشیم که یک آرایه ی رشته ای را بر اساس حروف الفبا مرتب می کند. برای مرتب کردن لیست ها، می توانیم از فنون مختلف با کارایی متفاوت، بسته به خصوصیات لیستی که می خواهیم مرتب شود، استفاده کنیم.
با استفاده از delegate ها، می توانیم مشخص کنیم که تابعی که باید استفاده کنیم، با پاس دادن یک الگوریتم تابع delegate به یک تابع مرتب سازی، مورد استفاده قرار گیرد.
با استفاده از delegateها، میتوانید با ارسال یک delegate تابع الگوریتم مرتبسازی به یک تابع مرتبسازی، تابع مورد استفاده را مشخص کنید.
By using delegates, you can specify the function to use by passing
a sorting algorithm function delegate to a sorting function.
از delegate ها استفاده های زیادی می شود، اما همان طور که قبلاً هم گفته شد، مهمترین کاربرد آنها در مدیریت رویداد(event handling) است که در فصل 13 توضیح داده می شود.
- بازدید: 49
1. سعی کنید نظرات شما مرتبط با مقاله ی مورد نظر باشد، در غیر این صورت پاسخ داده نخواهد شد.
2. سوالات خود را به صورت کوتاه بیان کنید و از پرسیدن چند سوال به طور همزمان خودداری کنید.
3. سوال خود را به طور واضح بیان کنید و از کلمات مبهم استفاده نکنید.