Դելեգատ (Delegate)

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

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

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

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

Դելեգատները կարող են դիմել ինչպես կլասի սովորական մեթոդին, այնպես էլ ստատիկ մեթոդին: Դելեգատ սահմանելու համար 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 դելեգատներում <> ում գրված ամենավերջին տիպը վերաբերում է վերադարձվող արժեքին, մնացած տիպերը ընդունվում են որպես ընդունող պարամետրերի տիպեր: