در این مقاله، ما در مورد delegate ها در #C صحبت میکنیم.
یک delegate، یک ارجاع به یک متد است. ما میتوانیم یک آبجکت delegate را برای پاس دادن آن به کدی که میخواهیم یک متد reference شده را فراخوانی کند استفاده کنیم بدون آنکه بدانیم در زمان کامپایل، کدام متد فراخوانی میشود.
درک این نکته مهم است که نوع برگشتی و تعداد پارامترهای متد مورد نظر باید با نوع برگشتی و تعداد پارامترهای delegate یکسان باشد. در غیراینصورت با خطای کامپایلر مواجه میشویم. ما در این مثال میتوانیم ببینیم که متد Write ما، همانند delegate تعریف شده، نوع برگشتی void و فقط یک پارامتر string دارد.
Delegate ها در کپسوله سازی متدهای ما خیلی مفید هستند.
#C دو Delegate توکار دارد: Func<T> و Action<T> که به طور گسترده ای مورد استفاده قرار میگیرد، پس بیشتر در مورد آنها صحبت میکنیم.
Func<T> Delegate
این delegate، یک متدی که حداکثر شانزده پارامتر دارد و مقداری از نوع مشخص را به عنوان خروجی برمی گرداند را کپسوله سازی میکند. به عبارتی دیگر، ما از Func delegate فقط با متدی که نوع برگشتی به غیر از void دارد استفاده میکنیم.
ما میتوانیم با استفاده از سینتکس زیر، Func delegate را نمونه سازی کنیم:
میبینیم که آخرین پارامتر داخل دو علامت کوچکتر و بزرگتر، یک نوع برگشتی میباشد. البته لزومی ندارد که آبجکت delegate را حتما به این شکل نمونه سازی کنیم.. بلکه میتوانیم به این شکل نیز این کار را انجام دهیم:
C#
1
Func<Type1,Type2...,ReturnType>name=MethodName;
حال با یک مثال ببینیم که چطور میتوانیم از Func delegate استفاده کنیم:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
classProgram
{
publicstaticintSum(inta,intb)
{
returna+b;
}
staticvoidMain(string[]args)
{
Func<int,int,int>sumDelegate=Sum;
Console.WriteLine(sumDelegate(10,20));
}
}
Action<T> Delegate
این delegate، یک متدی که حداکثر شانزده پارامتر دارد و هیچ نوع مقدار برگشتی ندارد را کپسوله سازی میکند. بنابراین ما میتوانیم فقط متدهایی که مقدار برگشتی void دارند را به این نوع از delegate اختصاص دهیم.
ما میتوانیم با استفاده از سینتکس زیر، Action delegate را نمونه سازی کنیم:
حال با یک مثال ببینیم که چطور میتوانیم از Action delegate استفاده کنیم:
C#
1
2
3
4
5
6
7
8
9
10
publicstaticvoidWrite(stringtext)
{
Console.WriteLine(text);
}
staticvoidMain(string[]args)
{
Action<string>writeDelegate=Write;
writeDelegate("String parameter to write.");
}
مثال کاربردی
در این مثال، ما قصد داریم که یک برنامه ایجاد کنیم که یکی از این سه متد (Sum، Subtract، Multiply) را بر اساس یک پارامتر ارائه شده اجرا کند. اساسا اگر ما Sum را به عنوان پارامتر استفاده کنیم متد Sum اجرا میشود و به همین ترتیب برای بقیه متدها. اول ما این مثال را بدون استفاده از delegate ها مینویسیم و سپس با استفاده از delegate ها، کدمان را refactor میکنیم.
پس با اولین قسمت، کارمان را شروع میکنیم:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
publicenumOperation
{
Sum,
Subtract,
Multiply
}
publicclassOperationManager
{
privateint_first;
privateint_second;
publicOperationManager(intfirst,intsecond)
{
_first=first;
_second=second;
}
privateintSum()
{
return_first+_second;
}
privateintSubtract()
{
return_first-_second;
}
privateintMultiply()
{
return_first*_second;
}
publicintExecute(Operation operation)
{
switch(operation)
{
caseOperation.Sum:
returnSum();
caseOperation.Subtract:
returnSubtract();
caseOperation.Multiply:
returnMultiply();
default:
return-1;//just to simulate
}
}
}
classProgram
{
staticvoidMain(string[]args)
{
varopManager=newOperationManager(20,10);
varresult=opManager.Execute(Operation.Sum);
Console.WriteLine($"The result of the operation is {result}");
Console.ReadKey();
}
}
اگر ما این برنامه را اجرا کنیم، برای هر عملیاتی که به سمت متد Execute میفرستیم پاسخ درستی را دریافت میکنیم. اما این کد میتواند خیلی بهتر شده و بدون عبارت switch-case خوانایی بیشتری داشته باشد. اگر ما بخواهیم که بیش از 10 operation (برای مثال) داشته باشیم، آنگاه استفاده از این بلاک switch جهت خواندن و همچنین نگهداری، میتواند خیلی افتضاح باشد.
بنابراین اجازه دهید تا کدمان را تغییر دهیم تا قابلیت خوانایی بیشتر، نگهداری راحت تر و همچنین ویژگی شی گرایی بیشتری داشته باشد. پس یک کلاس جدید به نام ExecutionManager اینجا معرفی میکنیم:
اینجا ما یک dictionary که تمام operation ها و ارجاعات به متدهایمان را در خود نگه میدارد را ایجاد میکنیم (Func delegate ها). حالا ما میتوانیم این کلاس را به داخل کلاس OperationManager تزریق کنیم و متد Execute را تغییر دهیم:
حالا ما همه چیز را درون سازنده کلاس OperationManager تنظیم نمودیم و اکشنمان را درون متد Execute در صورت مجاز بودن operation مورد نظر اجرا کردیم. در اولین نگاه، ما میبینیم که چقدر این کد بهتر به نظر میرسد.
Console.WriteLine($"The result of the operation is {result}");
Console.ReadKey();
}
}
نتیجه گیری
در این مقاله، ما موارد زیر را یاد گرفتیم:
نحوه نمونه سازی یک delegate
شیوه استفاده از delegate های Func و Action
نحوه کدنویسی بهتر با استفاده از delegate ها
اگر شما در تمام این سری از آموزش در سطح متوسط همراه ما بودید به شما تبریک میگیم چرا که در حال حاضر از دانش خوبی در زمینه مفاهیم شی گرایی در #C برخوردار هستید.
من امید عباسی هستم. سالهاست که در زمینه برنامه نویسی با تکنولوژی دات نت فعالیت میکنم و عاشق این هستم که تجربیات و دانش خودم را در این زمینه با دیگران به اشتراک بزارم.
خیلی دوست دارم که نظر و انتقاد خودتون رو در مورد این نوشته برای من بنویسید تا بتونم در آینده، مطالب بهتر و ارزشمندتری را برای شما فراهم کنم.
در صورت داشتن هرگونه سوال هم در قسمت دیدگاه ها میتونید با بنده در ارتباط باشید