Delegate أو المندوب ما هو إلا Object يقوم بحفظ عنوان Method معينة , ليتم إستعمال هذا الـ Object الذي يحتفظ بمؤشر إلى تلك الـ Method في تنفيذها عند الحاجة من قبل مستعمل الـ Delegate .
فلنأخذ المثال التالي , لنفهم الحاجة لوجود بنية كهذه :
Smart Printer يقدم خدمة طباعة قيمة أي تابع رياضي يأخذ قيمة int ويعيد قيمة Double ,ويقوم الـ Smart Printer بطباعة النتيجة و إسم التابع بطريقة أنيقة إلى الـ Console , فلنلقي نظرة إلى الشكل التالي :

فكما هو ملاحظ من الشكل السابق , الـ Smart Printer يظهر كمقدم خدمة (ألا و هي الطباعة إلى الـ Console ) إلى كل Object يحتاج إلى هذه الخدمة ,ولكي يٌخدَم اي Object من قبل الـ Smart Printer فإنه يحتاج إلى تطبيق الواجهة : ISmartPrint التي تحوي الـ Members التالية :
Calculate : سيقوم الـ Smart Printer بطلب هذه الـ Method التي ستحوي function لا يعرف محتواها مسبقا الـ Smart Printer , لكن ما يعرفه عنها يكفيه لتقديم الخدمة لها , فهو يعرف أنها تأخذ int وتعيد Double ...
Parameter : وهو الرقم الذي سيقوم بتمريرة الـ Smart Printer عند حساب قيمة الـ Function عند تلك القيمة ...
Name : و هو إسم الـ Function الرياضي الذي سيقوم الـ Printer بعرضه عند تقديم الخدمة لذلك التابع الرياضي (في عرضه بشكل جميل على الـ Console ) .
حاليا , لدينا زبونان يحتاجان إلى خدمة العرض و التنسيق الجميل على شاشة الـ Console ... إنهما التابعين الرياضيين : Factorial , Fibonacci وهما متمثلان في الـ Factorial , Fibonacci Class الذين يطبقان الـ ISmartPrint Interface كأحد شروط التخديم ...
فلنستعرض الكود الاّن :
public interface ISmartPrint
{
string Name { get; }
int Parameter { get; }
double Calculate(int num);
}والزبائن هما :
public class Fibonacci : ISmartPrint
{
public string Name
{
get { return "Fibonacci Function"; }
}
public int Parameter
{
get { return 10; }
}
public double Calculate(int num)
{
if (num <= 1)
{
return 1;
}
return Calculate(num - 1) + Calculate(num - 2);
}
}
public class Factorial : ISmartPrint
{
public string Name
{
get { return "Factorial Function"; }
}
public int Parameter
{
get { return 16; }
}
public double Calculate(int num)
{
if (num <=1)
{
return 1;
}
return num * Calculate(num - 1);
}
}وأما مقدم الخدمة فهو :
public class SmartPrinter
{
public SmartPrinter(ISmartPrint smartPrint)
{
this.SmartPrint = smartPrint;
}
public ISmartPrint SmartPrint { get; set; }
public void Print()
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("calculating {0} ...",SmartPrint.Name);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The Value of {0} with parameter : {1} is {2}",SmartPrint.Name,SmartPrint.Parameter,SmartPrint.Calculate(SmartPrint.Parameter));
Console.ForegroundColor = ConsoleColor.Gray;
}
}يقوم الـ Smart Printer بتلقي الـ Object المراد خدمته عبر الـ Constructor الخاص به , وعند إستدعاء Print يقوم بحساب قيمة التابع عبر تمرير الـ Parameter الموجود في نفس الـ Object المٌخدم ... ولكن بالتأكيد عرضه للمعلومات التي حصل عليها , سيحوي الكثير من البهرجة البصرية في شاشة Console تعيسة
...
كود الإختبار :
SmartPrinter printer = new SmartPrinter(new Factorial());
printer.Print();
Console.WriteLine("\nChanging the Smart Object...\n");
printer.SmartPrint = new Fibonacci();
printer.Print();
Console.ReadLine();ستظهر النتيجة كالاّتي :
calculating Factorial Function ...
The Value of Factorial Function with parameter : 16 is 20922789888000
Changing the Smart Object...
calculating Fibonacci Function ...
The Value of Fibonacci Function with parameter : 10 is 89
لكن مع بعض الألوان ...
رغم بساطة السيناريو , لكن لا بد من إعادة التلخيص بالأفكار التالية :
الـ Smart Printer قدم خدمة لزبائنة Fibonacci , Factorial الذين بدورهم إحتاجا لمن يقوم بعرض نتائجهم بطريقة جيدة ...
والاّن , فلننتقل لدراسة البُنى الجاهزة الموجودة في الـ .Net والمتمثلة بالكلمة المفتاحية Delegate ,فلنقم بتقديم الحل السابق ولكن بالحل الجديد المختصر :
public delegate double Func(int num); // here is the magic
class Program
{
static void Main(string[] args)
{
SmartPrinter printer = new SmartPrinter(new Func(Factorial), 11, "Factorial");
printer.Print();
Console.WriteLine("\nChanging the Smart Object...\n");
printer.Function = new Func(fibonacci);
printer.Name = "Fibonacci";
printer.Parameter = 6;
printer.Print();
}
private static double Factorial(int number)
{
if (number <= 1)
{
return 1;
}
return number * Factorial(number - 1);
}
private static double fibonacci(int number)
{
if (number <= 1)
{
return 1;
}
return fibonacci(number - 1) + fibonacci(number - 2);
}
}وبالتأكيد سيتم تعديل الـ Smart Printer ايضا :
public class SmartPrinter
{
public SmartPrinter(Func function, int parameter, string name)
{
this.Function = function;
this.Parameter = parameter;
this.Name = name;
}
public string Name { get; set; }
public int Parameter { get; set; }
public Func Function { get; set; }
public void Print()
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("calculating {0} ...", this.Name);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("The Value of {0} with parameter : {1} is {2}", this.Name, this.Parameter, this.Function.Invoke(Parameter));
Console.ForegroundColor = ConsoleColor.Gray;
}
}ما يجب ملاحظته هنا هو أننا قمنا بإلغاء فكرة أن الزبون يجب أن يتمثل في Object , بل قمنا بالإستغناء عن ذلك و عوضا عنه , قمنا بتمرير ثلاث معلومات أساسية إلى الـ Smart Printer هم : Function , Parameter ,Name وهم نفس الأعضاء الذين تواجدوا في الـ IsmartPrinter Interface التي تم الإستغناء عنها , الجميل هنا أننا إستطعنا تمرير إسم Function إلى الـ Smart Printer عن طريق الـ Func Delegate الذي تم تعريفة في السطر الأول كما يلي :
public delegate double Func(int num);
والذي يعبر عن مندوب يستطيع حمل إسم أي Method تحمل نفس توقيعه (أي الـ Parameters والـ Return Type ) , فقط نعامله على أنه Type عادي قابل للـ Instantiation (الإستمثال , فلنعد إلى الإنكليزية أفضل
) ...
فنستيطع عمل شيء كالتالي :
private static double Factorial(int number)
{
if (number <= 1)
{
return 1;
}
return number * Factorial(number - 1);
}
static void main() {
Func factorial = new Func(Factorial);
SmartPrinter printer = new SmartPrinter(factorial,11,"Facotiral");
printer.Print();
{فنلاحظ أننا قمنا بإستعمل الـ Func Delegate كأي Type و قمنا بالتأشير في الـ Constructor الخاص به إلى الـ Factorial Method ...
أكبر خطأ شائع عند إستعمال الـ Delegate هو إستدعاء الـ Method عند تمريرها , بدل تمرير إسمها فقط , فلم نقم في المثال السابق بكتابة شيء كالتالي :
Func factorial = new Func(Factorial()); // Will not compile ...
فكرة أخرى , لو لم يكن الـ Delegate بالحقيقة Type لما أمكن وضعه بمكان لا يتم فيه إلا تعريف Types , وللحقيقة هناك معلومة يجب ذكرها ,فالـ Delegate هو Class يرث من MultiCast Delegate Abstract Class , وبالتالي الـ #Compiler of C سيقوم بتوليد هذا الصنف من أجلنا ... ولا نستطيع مشاهدة هذه الحقيقة إلا بأدوات الـ Decompiling كالـ Reflector ...
طريقة إستدعائنا للـ Delegate في الامثلة السابقة تم بشيء يشبه التالي :
Delegate void MyDel(int num);
static void foo(int n){...}
static void main()
{
MyDel del = new MyDel(foo);
del();
}لكن , بما أن أي Delegate ما هو إلا Type قام الـ Compiler بإنشائه لنا , فنتوقع أن يحوي Members بالتأكيد , وأهم Member يهمنا هو Invoke الذي تعني إستدعاء أو مناشدة , فهي من تقوم بتنفيذ الـ Method الذي يؤشر لها الـ Delegate ...
فنستطيع كتابة شيء كالتالي :
del.Invoke(9);
وعمليا الـ Compiler سيقوم بتبديل كل (del(9 بـ (del.inovke(9 ...
بقية الـ Members ربما يتم التطرق لها في مقالات لاحقة ...
نلاحظ أن هناك الكثير من السهولة التي قدمت لنا عن طريق إستعمال Delegate فلم نعد بحاجة لعمل Interface لتقوم بدور العقد الذي يجب أن يحترمه الطرفان , فإستعضنا عنها بالـ Delegate Construct , والتي مكنتنا أيضا من كتابة فقط الـ Functions بشكل مباشر مثل الـ Factorial , Fibonacci اللتان إحتجنا إلى تغليفهما في Types كما في الحل الأول ... فكلما إستطعنا التخلص من المظاهر الرسمية ,إستطعنا التركير أكثر ...
إلى اللقاء في شرح الـ Anonymous Delegates و Lambda Expressions ...
لكن الأسئلة مباحة ... ![]()