Delegates

Delegates در سی شارپ روشی مطمئن و typesafe را برای بکار گیری مفهوم function pointer ارائه می دهند. یکی از ابتدایی ترین استفاده های function pointers پیاده سازی callback می باشد. اما در ابتدا لازم است تا با اصول اولیه ی کاری آن آشنا شویم.

مثال یک :
یک delegate چگونه تعریف و استفاده می شود؟
Delegate یک شیء است که بیانگر یک تابع می باشد بنابراین می تواند بعنوان آرگومان ورودی یک تابع دیگر و یا عضوی از یک کلاس بکار رود.
در زبان "function-pointer" ، Func1() اشاره گری به Func2() را بعنوان پارامتر دریافت کرده و نهایتا آنرا فراخوانی می کند.
در زبان "delegate" ، Func1() یک شیء delegate از Func2() را دریافت کرده و سپس آنرا فراخوانی می کند.
در مثال زیر از دو تابع برای شرح این مطلب سود جسته شده است:
Func1() از delegate استفاده می کند.
Func2() یک delegate است.

( شماره گذاری خطوط ، در کد زیر ، صرفا برای راحت تر شدن توضیحات در مورد آنها است و لزومی به تایپ آنها در برنامه ی اصلی نیست. )



کد:
01 using System;
02 delegate void Delg(string sTry);
03 public class Example1{

    // function which uses the delegate object
04  private static void Func1(Delg d){
05   d("Passed from Func1");
06  }

    // function which is passed as an object
07  private static void Func2(string sToPrint){
08   Console.WriteLine("{0}",sToPrint);
09  }

    // Main execution starts here
10  public static void Main(){
11   Delg d = new Delg(Func2);
12   Func1(d);  
13  }
14 }
LINE 02
یک شیء delegate را برای Func2 تعریف می کند.


LINE 04-06
تابعی را تعریف کرده است که آرگومان ورودی آن از نوع Delg است.

LINE 07-09
تابعی را تعریف می کند که باید به صورت delegate به تابع دیگر فرستاده شود.

LINE 10-14
تابع Main اجرای برنامه را با ایجاد یک شیء delegate برای Func2 آغاز کرده و سپس تابع Func1 را فراخوانی می کند.




مثال 2:
چگونه می توان از delegates در کارهای عملی استفاده کرد؟

طرح یک مساله:
شخصی تقاضای ثبت نام در یک مؤسسه ی آموزشی و همچنین تقاضای کاریابی در یک شرکت را داده است. هر کدام از این نهادها روشی خاص خود را برای ارزیابی شخص دارند.

راه حل (با روشی شیء گرا):
شخص مشخصاتی همچون سن / جنس / میزان تحصیلات قبلی / تجربیات کاری و مدارک مرتبط دارد.
مؤسسه ی آموزشی تعدادی از این مشخصات را برای ارزیابی شخص استفاده می کند و این امر در مورد شرکت یاد شده نیز صادق است.
شیء شرکت و شیء آموزشگاه هر کدام توابع ارزیابی خاص خودشان را پیاده سازی می کنند.
شخص ، اینترفیسی واحدی را در اختیار شرکت / آموزشگاه برای ارزیابی خود قرار می دهد.

پیاده سازی (با استفاده از سی شارپ):
ما delegate‌ایی را تعریف می کنیم که بیانگر اینترفیسی است که به شرکت و آموزشگاه اجازه ی چک کردن شخص را می دهد.
سه کلاس school و company و person را تعریف می نماییم.
کلاس test را برای آزمودن این موارد ایجاد می کنیم.




کد:
01 using System;
02 using System.Collections;

03 public delegate bool GetChecker(Person p);

   // Person has his information with him as he 
   // applies for School and Company
04 public class Person
05 {
06  public string Name; 
07  public int Age;
08  public bool Graduate;
09  public int YearsOfExp;
10  public bool Certified;
 
11  public Person(string name,
                  int age,
                  bool graduate,
                  int yearsOfExp,
                  bool certified)
12  {
13   Name=name;
14   Age=age;
15   Graduate=graduate;
16   YearsOfExp=yearsOfExp;
17   Certified=certified;
18  }
19  public bool CheckMe(GetChecker checker)
20  {
21   return(checker(this));
22  }
23 }

   // A school, the person applied for higher studies
24 public class School
25 {
26  public static bool SchoolCheck(Person p)
27  {
28   return (p.Age>10 && p.Graduate);
29  }
30 }

   // A Company, the person wants to work for
31 public class Company
32 {
33  public static bool CompanyCheck(Person p)
34  {
35   return (p.YearsOfExp>5 && p.Certified);
36  }
37 }

   // A Test class, displays delegation in action
38 public class Test
39 {
40 public static void Main()
41 {
42 Person p1 = new Person("Jack",20,true,6,false);
43 Console.WriteLine("{0} School Check : {1}",
p1.Name,
p1.CheckMe(new GetChecker(School.SchoolCheck)));
44 Console.WriteLine("{0} Company Check : {1}",
p1.Name,
p1.CheckMe(new GetChecker(Company.CompanyCheck)));
45 }
46 }

LINE 03
Delegate مورد نیاز را تعریف می کند.

LINE 04-23
کلاس person را تعریف می کند. این کلاس تابعی پابلیک را ارائه می دهد که آرگومان ورودی آن از نوع GetChecker می باشد.

LINE 24-30
کلاس school را تعریف می کند و سپس تابعی را که delegate است ارائه می دهد.


LINE 31-37
کلاس company را تعریف می کند و سپس تابعی را که delegate است ارائه می دهد.


LINE 38-36
کلاس test را پیاده سازی می نماید. سپس یک شیء شخص ساخته می شود. در ادامه new GetChecker(School.SchoolCheck) و new GetChecker(Company.CompanyCheck) شیء ایی را ایجاد می کند از نوع delegate مورد نیاز و آنرا به تابع CheckMe می فرستد. خروجی نتیجه ی ارزیابی این شخص می باشد.

اگر چک کردن اشخاص بیشتری نیاز باشد به این صورت عمل می شود:
کد:
Person p1 = new Person("Jack",20,true,6,false);
Person p2 = new Person("Daniel",25,true,10,true);
GetChecker checker1= new GetChecker(School.SchoolCheck);
GetChecker checker2= new GetChecker(School.CompanyCheck);

Console.WriteLine("{0} School Check  : {1}", 
                        p1.Name,p1.CheckMe(checker1));
Console.WriteLine("{0} Company Check : {1}", 
                        p1.Name,p1.CheckMe(checker2));
Console.WriteLine("{0} School Check  : {1}", 
                        p2.Name,p2.CheckMe(checker1));
Console.WriteLine("{0} Company Check : {1}", 
                        p2.Name,p2.CheckMe(checker2));

مثال 3 :
Delegates در تعامل بین دات نت فریم ورک و سی شارپ چه نقشی دارد؟

طرح یک مساله:
نمایش دادن میزان پیشرفت خواندن یک فایل هنگامی که حجم فایل بسیار زیاد است.

راه حل ( با استفاده از سی شارپ):
در مثال زیر از کلاس FileReader برای خواندن یک فایل حجیم استفاده شده است. هنگامیکه برنامه مشغول خواندن فایل است 'Still reading.. را نمایش می دهد و در پایان 'Finished reading..'. را عرضه می کند.
برای اینکار از فضای نام System.IO استفاده شده است. این فضای نام حاوی delegate ایی مهیا شده برای ما می باشد. بدین ترتیب می توانیم به دات نت فریم ورک بگوییم که ما تابعی را تعریف کرده ایم که او می تواند آنرا فراخوانی کند.
سؤال: چه نیازی وجود دارد تا دات نت فریم ورک تابع ما را فراخوانی و اجرا کند؟ با استفاده از تابع ما که دات نت فریم آنرا صدا خواهد زد در طول خواندن فایل به ما گفته می شود که بله! من هنوز مشغول خواندن هستم! به این عملیات Callback نیز گفته می شود. به اینکار پردازش asynchronous نیز می گویند!





کد:
01  using System;
02  using System.IO;

03  public class FileReader{
04   private Stream sInput;
05   private byte[] arrByte;
06   private AsyncCallback callbackOnFinish;
     
07   public FileReader(){
08    arrByte=new byte[256];  
09    callbackOnFinish = new AsyncCallback(this.readFinished);
10   }
     
11   public void readFinished(IAsyncResult result){
    
12    if(sInput.EndRead(result)>0){  
13    sInput.BeginRead(arrByte,
                       0,
                       arrByte.Length,
                       callbackOnFinish,
                       null);
14    Console.WriteLine("Still reading..");
15   }
16   else   Console.WriteLine("Finished reading.."); 
17  }
 
18  public void readFile(){
19   sInput = File.OpenRead(@"C:\big.dat");
20   sInput.BeginRead(arrByte,
                      0,
                      arrByte.Length,
                      callbackOnFinish,
                      null);
21   for(long i=0;i<=1000000000;i++){
      // just to introduce some delay
22   }
23  }
 
24  public static void Main(){
25   FileReader asynctest=new FileReader();
26   asynctest.readFile();   
27  }  
28 }

LINE 02
فضای نام System.IO را به برنامه ملحق می کند. این فضای نام به صورت خودکار حاوی تعریف delegate زیر می باشد:
کد:
کد:
public delegate void AsyncCallback (IAsyncResult ar);
LINE 03-10
تعریف کلاس


LINE 06
شیء delegate را تعریف می کند.

LINE 07-10
سازنده ی کلاس را پیاده سازی می کنند. در اینجا ما تصمیم گرفته ایم که بافری حاوی 256 بایت را در هر لحظه بخوانیم.

LINE 09
شیء delegate نمونه سازی شده است.

LINE 18-23
readFile را پیاده سازی می کند.

LINE 12-16
نحوه ی استفاده از شیء IAsyncResult را بیان می کند.

LINE 12
sInput.EndRead(result) تعداد بایتهای خوانده شده را بر می گرداند. این خواندن تاجایی که تعداد بایتهای خوانده شده صفر است ادامه پیدا می کند و در اینجا 'Finished reading..' اعلام می گردد.