Դելեգատ (ծրագրավորում)

Վիքիպեդիայից՝ ազատ հանրագիտարանից

Դելեգատը դա կլաս է, որը հնարավորություն է տալիս նշել մեկ կամ ինչու չէ նաև մի քանի մեթոդներ, որոնք կկանչվեն ավելի ուշ։ Դելեգատի օբյեկտը պահում է հետևյալ ինֆորմացիան՝

• Մեթոդի անունը, որը պետք է կանչվի

• Այդ մեթոդի ընդունող արժեքները, եթե կան

• Մեթոդի վերադարձնող արժեքը

Դելեգատները կարող են դիմել ինչպես կլասի սովորական մեթոդին, այնպես էլ ստատիկ մեթոդին։ Դելեգատ սահմանելու համար C# ում օգտագործվում է delegate բառը։ Դելեգատը կարող է ունենալ ցանկացած անուն միայն թե, այն իր կառուցվածքով պետք է համապատասխանի այն մեթոդի հետ, որն այն կիրառելու է։ Բերենք դելեգատի օրինակ

public delegate int BinaryOp(int x, int y);
public class SimpleMath
{
public static int Add(int x, int y)
{
return x + y;
}
public static int Subtract(int x, int y)
{
return x + y;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("-----------------------------------");
BinaryOp b = new BinaryOp(SimpleMath.Add);
Console.WriteLine(" 5 + 5 is {0}",b(5,5));
Console.ReadLine();
}
static void DisplayDelegateInfo(Delegate obj)
{
foreach (Delegate item in obj.GetInvocationList())
{
Console.WriteLine("Method name = {0}",item.Method);
Console.WriteLine("Method type = {0}", item.Target);
// այս դեպքում կլասի անունը որպես տիպ / Method type / չի նշվում քանի որ 
//մեթոդը ստատիկ է, եթե ստատիկ չլիներ ապա այն կունենար օբյեկտ որը ցույց 
//կտար տվյալ մեթոդը, հետևաբար որպես տիպ կնշեր օբյեկտի տիպը
}
}
}

Տվյալ օրինակում նախ սահմանում ենք դելեգատ int վերադարձնող արժեքով, և երկու int տիպի ընդունող արժեքով։ Ապա SimpleMath կլասում հայտարարում ենք երկու մեթոդ, որոնք ունեն նույն կառուցվածքը ինչ դելեգատը, հակառակ դեպքում դելեգատի միջոցով այն հնարավոր չի լինի կանչել։ Ապա main-ում ստեղծելով դելեգատի օբյեկտ կանչում ենք համապատասխան մեթոդը։ Արդյունքում մենք կարողացանք կանչել մեթոդը դելեգատի միջոցով , որը հնարավորություն է տալիս SimpleMath.Add մեթոդը փոխելով SimpleMath.Subtract ստանալ հանման գործողությունը։ Դելեգատը հնարավորություն է տալիս մեթոդին որպես պարամետր փոխանցել մեթոդ։ Որպեսզի իմանանք թե տվյալ դելեգատը ինչ մեթոդեր ունի պետք է կանչել GetInvocationList(), որը վերադարձնում է դելեգատի մեթոդները։ Մեթոդի անվանումը կարելի է ստանալ Method, իսկ տիպը Target գործիքների միջոցով։

Դելեգատի ներքին նկարագրություն

Երբ C#-ի կոմպիլիատորը հանդիպում է դելեգատի, այն գեներացնում է հատուկ կառուցվածքով կլաս, որը թույլ է տալիս պահել մեթոդների ցանկը, որոնց պետք է դիմի հետագայում։ Օրինակ Public delegate string MyDelegate (bool a, int b); դելեգատի դեպքում կոմպիլիատորը կստեղծի կլաս նույն անունով (MyDelegate)

sealed class MyDelegate։System.MulticastDelegate
{
public string Invoke(bool a, int b);
public IAsyncResult BeginInvoke(bool a, int b, AsyncCallback cb, object state);
public string EndInvoke(IAsyncResult result);
}

Դելեգատը որպես պարամետր կարող է ունենալ նաև ref out պարամետրեր։

Դիտարկենք հետևյալ դեպքը, երբ ունենք Աշխատակիցներ կլասը, որն ունի PromoteEmployee /կոչում տալ աշխատակցին/ մեթոդը, և քանի որ աշխատակիցներից պետք է ընտրվեն նրանք ովքեր բավարում են տվյալ պաշտոնին հատուկ պահանջներին, ունենք պայման, որի ստուգումը կատարվում է ֆունկցիայի միջոցով, վերջինս մեթոդին փոխանցելու հնարավորություն է տվել դելեգատը։ Ցանկացած պահին փոխելով փոխանցվող ֆունկցիան /տվյալ դեպքում պայմանը/ կստանանք արդյունք տարբեր պաշտոնների համար։ Կոդը ավելի հակիրճ գրելու նպատակով կիրառվել է նաև լյամբդա => հասկացությունը, ինչը հնարավորություն է տալիս փոխանցել ոչ թե մեթոդը, այլ անմիջապես մեթոդի մարմինը` դելեգատին հատուկ կառուցվածքով։

delegate bool checking(Employee emp);
class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public int Experience { get; set; }
public static void PromoteEmployee(List<Employee> ListEmp, checking IsEligibleToPromote)
{
foreach (Employee item in ListEmp)
{
if (IsEligibleToPromote(item))
Console.WriteLine("Promote- > {0} with {1} years experiences", item.Name, item.Experience);  
}
}
}
class Program
{
static void Main(string[] args)
{
List<Employee> Emps=new List<Employee>() ;
Emps.Add(new Employee (){ID=1,Name="Ann",Experience=5});
Emps.Add(new Employee (){ID=2,Name="Suzi",Experience=1});
Emps.Add(new Employee (){ID=3,Name="Sevak",Experience=2});
Emps.Add(new Employee (){ID=4,Name="Lusine",Experience=3});
Emps.Add(new Employee (){ID=5,Name="Artur",Experience=6});
// 1.
Employee.PromoteEmployee(Emps, IsEligibleToPromote);
Console.WriteLine("\n Calling the same methos with using lyambda ... \n");
// 2. կամ մեթոդը կարելի է կանչել օգտագործելով լյամբդաի հասկացողությունը, ինչը 
// թույլ է տալիս փոխանցել ոչ թե ֆունկցիան, այլ նրա մարմինը, այսպես`
Employee.PromoteEmployee(Emps, emp => emp.Experience >= 5);
Console.ReadLine();
}
private static bool IsEligibleToPromote(Employee emp)
{
if (emp.Experience >= 5)
return true;
return false;
}
}

Բազմահասցե դելեգատ (multicast delegate)

Բազմահասցե կոչվում են այն դելեգատները, որոնք ունեն մեկից ավելի ֆունկցիաներին հղումներ։ Երբ կանչվում է դելեգատը բոլոր ֆունկցիաները, որին այն ունի հղումներ կանչվում են։ Գոյություն ունի բազմահասցե դելեգատ ստեղծելու երկու տարբերակ

• + կամ += գրանցել մեթոդը դելեգատում

• - կամ -= հեռացնել մեթոդը դելեգատից

delegate void SampleDelegate();

// 1.
SampleDelegate del1 = new SampleDelegate(SampleMethodOne);
SampleDelegate del2 = new SampleDelegate(SampleMethodTwo);
SampleDelegate del3 = new SampleDelegate(SampleMethodThree);
SampleDelegate del = del1 + del2 + del3 - del2; 
//2.
SampleDelegate del = new SampleDelegate(SampleMethodOne);
del += SampleMethodTwo;
del += SampleMethodThree;

Եթե դելեգատը ունի վերադարձնող արժեք, և դա բազմահասցե դելեգատ է, ապա միայն վերջին մեթոդի արժեքը կվերադարձնի։ Նույնը կարելի է ասել նաև ref և out տեսակի պարամետր փոխանցելու դեպքում։ Օրինակ

static void Main(string[] args)
{
SampleDelegate del = new SampleDelegate(SampleMethodOne);
del += SampleMethodTwo;
del += SampleMethodThree; 
int b=200;
del(out b);
Console.WriteLine(b);}
static void SampleMethodTwo(out int a)
{
a= 2;
}
static void SampleMethodThree(out int a)
{
a= 105;
}

Այս դեպքում արդյունքը կլինի 105 քանի որ վերջին ֆունկցիան, որը գումարել ենք ունի 105 արժեքը։ Սա այն դեպքն է, երբ գումարելիների տեղը փոխելիս գումարը փոխվում է, թեև իրականում այսպիսի դեպքերի համար գումարում չի կատարվում։ += և -= գործողությունները համապատասխանում են Delegate.Combine և Delegate.Remove մեթոդներին։ Այսպիսի դելեգատները հաճախ օգտագործվում են Design Patternներում։

Ընդհանրացված դելագատներ

Ընդհանրացված դելեգատը կազմվում է <T> ի միջոցով, ինչը հնարավորություն է տալիս ոչ թե նախապես որոշել դելեգատի ընդունող պարամետրի տիպը, այլ կիրառելիս` կանչելիս։ Դիտարկենք մեկ ընդհանրացված դելեգատի օրինակ public delegate void MyGenericDelegate<T>(T arg) Սա նշանակում է, որ այս դելեգատի միջոցով կանչվող մոթոդը պետք է ունենա void վերադարձնող արժեք և T տիպի ընդունող պարամետրեր, ընդ որում վերջինս կարող է լինել ցանկացած տիպ, ինչպես օրինակ 1. MyGenericDelegate strdel = new MyGenericDelegate<string>(MyMethodwithstring) Static void MyMethodwithstring(string s) { Console.WriteLine(s); } 2. MyGenericDelegate intdel = new MyGenericDelegate<int>(MyMethodwithint) Static void MyMethodwithint(int i) { Console.WriteLine(i.ToString()); }

Action<> և Func<> ընդհանրացված դելեգատներ

Գոյություն ունեն Action<> և Func<> ընդհանրացված դելեգատները, որոնք սահմանված են System անունների տարածքում։ Action<> դելեգատը կարող է կիրառել այն մեթոդները, որոնք ունեն void վերադարձնող արժեք, և կարող են ընդունել մինչև 16 պարամետր, ընդ որում պարամետրերի տիպերը կարող են լինել տարբեր։ Օրինակ

static void DisplayMessage(string msg, ConsoleColor txtColor, int printCount)
{
Console.ForegroundColor = txtColor;
for (int i = 0; i < printCount; i++)
{
Console.WriteLine(msg);
}
}

Այս մեթոդը կանչող դելեգատը սահմանվում է այսպես`

Action<string, ConsoleColor, int> actDel = new Action<string,ConsoleColor,int>(DisplayMessage);  
actDel("Hello world", ConsoleColor.Green, 3);

Հիշենք որ Action<> դելեգատի դեպքում մեթոդը անպայման մետք է ունենա void վերադարձնող արժեք, հակառակ դեպքում կարելի է կիրառել Func<> ընդհանրացված դելեգատը, որը ևս կարող է ընդունել ընդուպ մինչև 16 պարամետր, և ի տարբերություն նախորդի ունի հատուկ սահմանված վերադարձնող արժեք։

Դիտարկենք հետևյալ օրինակը
static string SumToString(int x, int y)
{
return (x + y).ToString();
}

Այս մեթոդը կանչող դելեգատը կունենա հետևյալ տեսքը`

Func<int, int, string> funcDel = new Func<int, int, string>(SumToString);
Console.WriteLine(funcDel(10,20));

Ինչպես արդեն նկատեցիք, Func<int, int, string> ինչպես և առհասարակ բոլոր Func դելեգատներում <> ում գրված ամենավերջին տիպը վերաբերում է վերադարձվող արժեքին, մնացած տիպերը ընդունվում են որպես ընդունող պարամետրերի տիպեր։