البحث في الموقع
المحتوى عن 'delegate'.
-
يُعتبر مفهوم النوّاب من المفاهيم الأساسيّة في سي شارب، وستستخدمه بشكل أو بآخر في أيّ برنامج حقيقي مكتوب بهذه اللغة. سنتحدّث في هذا الدرس عن هذا الموضوع المهمّ، وسنتعرّض في درس لاحق إلى مفهوم مهم آخر مرتبط به، ألا وهو مفهوم الأحداث Events. في جميع البرامج التي صادفناها في هذه السلسلة كنّا نعمل على استدعاء توابع وخصائص من كائنات ننشئها من الأصناف المختلفة. في البرمجة العمليّة، يحتاج الكائن في الكثير من الأحيان إلى وسيلة يفعل فيها العكس. أي يحتاج الكائن إلى أسلوب يستطيع من خلاله استدعاء تابع ما لا ينتمي إليه وذلك من تابع أو خاصيّة موجودة داخل الكائن. تُسمّى هذه الظاهرة بالردود callbacks، وهي تقنيّة مهمّة جدًّا مستخدمة حتى في أنظمة التشغيل على نحو واسع. ربما قد تتساءل لماذا يحتاج الكائن لأن يستخدم مثل هذه التقنيّة (الردود). الجواب على ذلك بسيط، فقد يحتاج الكائن إلى التعبير عن حالته الداخليّة، أو عن أمر طارئ قد حدث له، ففي تطبيقات سطح المكتب desktop applications مثلًا، يُعتبر أيّ زر موجود على نافذة البرنامج عبارة عن كائن. فعندما ينقر مستخدم البرنامج هذا الزر، فمن الطبيعي أن يبلّغنا الزر عن "حدث" النقر عليه. فكيف يفعل ذلك؟ يفعل ذلك ببساطة عن الطريق الردود التي نسمّيها في سي شارب بالنوّاب (ستعرف سبب التسمية بعد قليل). التصريح عن نائب يمكنك اعتبار النائب كصنف مستقل بحد ذاته، مع أنّ التصريح عنه يختلف عن التصريح عن الأصناف. تُصرّح الشيفرة التالية عن النائب SumDelegate: public delegate int SumDelegate(int a, int b); النوّاب -وعلى سبيل التبسيط- عبارة عن مغلّفات للتوابع. فالنائب SumDelegate السابق يمكنه أن يغلّف أو يشير إلى أيّ تابع آخر بشرط أن يقبل وسيطين من نوع int ويُرجع قيمة من نوع int أيضًا (لاحظ الشيفرة السابقة). فإذا أزلنا الكلمة المحجوزة delegate من الشيفرة السابقة، سيبدو الأمر "كما لو أنّنا" نصرّح عن التابع SumDelegate الذي يقبل وسيطين من النوع int ويُرجع قيمة من النوع int، وهو بمحدّد وصول public. لفهم الموضوع بشكل أفضل دعنا نستخدم النائب السابق في البرنامج Lesson13_01: 1 using System; 2 3 namespace Lesson13_01 4 { 5 public delegate int SumDelegate(int a, int b); 6 7 class Program 8 { 9 static void Main(string[] args) 10 { 11 SumDelegate handler = new SumDelegate(Sum); 12 13 int result = handler(5, 6); 14 15 Console.WriteLine(result); 16 } 17 18 static int Sum(int a, int b) 19 { 20 return a + b; 21 } 22 } 23 } لاحظ أولًا أنّنا وضعنا التصريح عن النائب SumDelegate في السطر 5 خارج أي صنف، مع أنّه كان ممكنًا أن نصرّح عنه ضمن الصنف Program. صرّحنا في الأسطر من 18 إلى 21 عن تابع ساكن static ضمن الصنف Program اسمه Sum يقبل وسيطين من النوع int ويُرجع قيمة من النوع int أيضًا. العمليّة التي يقوم بها هذا التابع بسيطة، فهو يجمع قيمتي الوسيطين الممرّرين إليه ويُرجع الناتج بسطرٍ واحد. انظر الآن إلى السطر 11 ضمن التابع Main. ستلاحظ أنّنا نصرّح عن المتغيّر handler من النوع (النائب) SumDelegate، ونُسند إليه كائنًا من نفس النوع باستخدام التعبير (new SumDelegate(Sum حيث مرّرنا إلى بانيته الوسيط Sum. ولكن أليس الوسيط Sum هو نفسه اسم التابع الساكن المصرّح عنه ضمن الصنف Main! في الواقع تتطلّب بانية النائب SumDelegate (على اعتباره صنفًا) وسيطًا عبارة عن تابع يقبل وسيطين من النوع int ويرُجع قيمة من النوع int، أي بشكل مماثل لتصريح النائب SumDelegate في السطر 5. انظر إلى العبارة البرمجيّة التالية في السطر 13: int result = handler(5, 6); أصبح بإمكاننا الآن أن نستدعي النائب handler بوسيطين من النوع int، وسيُرجع بالتأكيد قيمة من النوع int أيضًا. إذًا أصبح handler "ينوب" عن التابع Sum في عمليّة الاستدعاء، رغم أنّه في حقيقة الأمر سيستدعي التابع Sum ولكن من وراء الكواليس! يمكن باستخدام هذه التقنيّة وبإجراء بعض التعديلات الطفيفة (كما سنرى في الأحداث Events)، أن ينوب handler عن أكثر من تابع بنفس الوقت بشرط أن يقبل كلّ منها وسيطين من نوع int ويُرجع كلّ منها قيمة من النوع int. استخدام النواب بشكل عملي لنتناول برنامجًا يوضّح استخدام النوّاب بشكل عمليّ أكثر. انظر البرنامج Lesson13_02: 1 using System; 2 3 namespace Lesson13_02 4 { 5 public class Car 6 { 7 public delegate void SpeedNotificatoinDelegate(string message); 8 9 private SpeedNotificatoinDelegate speedNotificationHandler; 10 11 public void RegisterWithSpeedNotification(SpeedNotificatoinDelegate handler) 12 { 13 this.speedNotificationHandler = handler; 14 } 15 16 public int CurrentSpeed { get; set; } 17 public int MaxSpeed { get; set; } 18 19 public Car() 20 { 21 CurrentSpeed = 0; 22 MaxSpeed = 100; 23 } 24 25 public Car(int maxSpeed, int currentSpeed) 26 { 27 CurrentSpeed = currentSpeed; 28 MaxSpeed = maxSpeed; 29 } 30 31 public void Accelerate(int delta) 32 { 33 CurrentSpeed += delta; 34 35 if (CurrentSpeed > MaxSpeed) 36 { 37 if(this.speedNotificationHandler != null) 38 { 39 string msg = string.Format("You exceed the maximum speed! (Current = {0}, Max = {1})", 40 CurrentSpeed, MaxSpeed); 41 42 speedNotificationHandler(msg); 43 } 44 } 45 } 46 } 47 48 class Program 49 { 50 static void Main(string[] args) 51 { 52 Car car = new Car(100, 0); 53 54 car.RegisterWithSpeedNotification(new Car.SpeedNotificatoinDelegate(OnExceedMaxSpeedHandler)); 55 56 for(int i = 0; i < 5; i++) 57 { 58 Console.WriteLine("Increasing speed by 30"); 59 car.Accelerate(30); 60 } 61 } 62 63 static void OnExceedMaxSpeedHandler(string message) 64 { 65 Console.WriteLine(message); 66 } 67 } 68 } نفّذ هذا البرنامج لتحصل في الخرج على ما يلي: Increasing speed by 30 Increasing speed by 30 Increasing speed by 30 Increasing speed by 30 You exceed the maximum speed! (Current = 120, Max = 100) Increasing speed by 30 You exceed the maximum speed! (Current = 150, Max = 100) خذ نفسًا عميقًا، وجهّز كوبًا من الشاي (شرابي المفضّل) لندخل في تفاصيل البرنامج. ينقسم هذا البرنامج إلى صنفين: الصنف Car (من السطر 5 حتى السطر 46) والصنف Program (من السطر 48 حتى السطر 67). يمثّل الصنف Car القالب العام لسيّارة، ويحتوي على خاصيّتين CurrentSpeed و MaxSpeed وتعبّران عن السرعة الحالية والسرعة القصوى على الترتيب. كما يحتوي على بانيتين، إحداهما عديمة الوسائط، والثانية ذات وسيطين من نوع int لسهولة الإنشاء والإسناد للخصائص. يضم الصنف Car أيضًا النائب SpeedNotificatoinDelegate (السطر 7). تذكّر أنّه من الممكن أن تكون النوّاب ضمن الأصناف. يمكن لهذا النائب أن يغلّف أي تابع يقبل وسيطًا واحدًا من النوع string ولا يرجع أي قيمة (void). نعرّف أيضًا ضمن الصنف Car الحقل speedNotificationHandler (السطر 9) من النوع SpeedNotificatoinDelegate وذو محدّد وصول private، وأخيرًا يحتوي الصنف Car على التابعين Accelerate الذي يعبّر عن إكساب السيّارة المزيد من السرعة، وهو يتطلّب وسيطًا واحدًا من النوع int ويمثّل مقدار الزيادة في السرعة. والتابع RegisterWithSpeedNotification الذي يتطلّب وسيطًا واحدًا من النوع SpeedNotificatoinDelegate. وظيفة هذا التابع هو استقبال وسيط من النوع (النائب) SpeedNotificatoinDelegate وإسناده إلى الحقل الخاص speedNotificationHandler. سيغلّف النائب الممرّر لهذا التابع تابعًا آخر يقبل وسيطًا واحدًا من النوع string ولا يُرجع أي قيمة (void). يحتوي الصنف Program على تابعين: الأوّل هو Main (من السطر 50 حتى السطر 61) وهو غنيّ عن التعريف. والثاني هو OnExceedMaxSpeedHandler (من السطر 63 حتى السطر 66) الذي يحتاج إلى وسيط نصي واحد، ولا يرجع شيء (void)، وهو يحتوي على عبارة برمجيّة وحيدة وظيفتها عرض قيمة الوسيط النصيّ msg على الشّاشة. يبدأ البرنامج في التابع Main بالتصريح عن المتغيّر car وإسناد كائن جديد إليه من النوع Car، حيث نمرّر 100 كسرعة قصوى، والقيمة 0 كسرعة حاليّة إلى بانية الصنف Car. بعد ذلك سيعمل البرنامج على إنشاء كائن نائب يغلّف التابع OnExceedMaxSpeedHandler من خلال التعبير: new Car.SpeedNotificatoinDelegate(OnExceedMaxSpeedHandler) سيمرَّر هذا الكائن الجديد إلى التابع RegisterWithSpeedNotification كما هو واضح من السطر 54. يدخل البرنامج بعد ذلك في حلقة for (الأسطر من 56 حتى 60) التي ستتكرّر 5 مرّات، وبعد أن يتم استدعاء التابع Accelerate أربعة مرّات (السطر 59)، تصبح عندها السرعة الحالية تساوي 120 وهي أكبر من السرعة القصوى، لذلك يُستدعى التابع الذي يغلّفه النائب speedNotificationHandler (السطر 42) بالشكل: speedNotificationHandler(msg); والذي هو في حالتنا التابع OnExceedMaxSpeedHandler ليمرّر إليه رسالة نصيّة ضمن المتغيّر msg توضّح بأنّه قد تمّ تجاوز السرعة القصوى المحدّدة. وستتكرّر نفس العمليّة من أجل الدورة الخامسة والأخيرة للحلقة for. لاحظ الاختبار الذي أجريناه في السطر 37 من التابع Accelerate. يتأكّد هذا السطر من أنّ الحقل speedNotificationHandler ليس فارغًا (يحوي null). لأنّه إذا كان يحوي null فلا ينبغي تنفيذ عبارة الاستدعاء في السطر 42. أنصح بأن تستخدم تطبيق Visual Studio من أجل هذا البرنامج، حيث يمكن أن تستفيد من المنقّح Debugger الخاص به لتنفيذ البرنامج خطوة بخطوة (استخدم المفتاح F11 لتنفيذ البرنامج بشكل خُطَويّ) لفهم أفضل له. ملاحظة: أيّ حقل مصرّح عنه ضمن صنف ما ويكون من نوع مرجعيّ reference type تكون القيمة الافتراضيّة له هي null في حال لم نُسند إليه أي قيمة عند التصريح عنه. وتعني null أنّ هذا الحقل لا يحتوي على مرجع لأيّ كائن. ويسري نفس الأمر على المتغيّرات المحليّة التي نصرّح عنها ضمن التوابع والتي تكون أيضًا من أنواع مرجعيّة. ملاحظة: يمكن لأي نائب أن يغلّف توابع عاديّة أو توابع ساكنة static. تمارين داعمة تمرين 1 أجرِ تعديلًا على البرنامج Lesson13_02 بحيث يستخدم البرنامج النائب speedNotificationHandler لإرسال تنبيه إلى التابع الذي يُغلّفه هذا النائب، في حال وصلت السرعة الحالية إلى منتصف السرعة القصوى أو تجاوزتها (التنبيه يجب أن يصدر لمرّة واحدة). (تلميح: ستحتاج إلى التصريح عن حقل خاص ضمن الصنف Car من نوع bool مثلًا لكي يعرف البرنامج أنّه قد أصدر التنبيه الخاص بالوصول إلى منتصف السرعة القصوى، لكيلا يعيد إصدار مثل هذا التنبيه مرّة أخرى). تمرين 2 أجرِ تعديلًا آخرًا على البرنامج Lesson13_02 بحيث تستغني فيه عن استخدام التابع RegisterWithSpeedNotification. دون التغيير في أسلوب عمل البرنامج. (تلميح: ستحتاج إلى جعل الحقل speedNotificationHandler ذو محدّد وصول public بدلًا من private، ثم تتعامل مع هذا الحقل مباشرةً من الصنف Main). الخلاصة تعرّفنا في هذا الدرس على مفهوم جديد لكنّه أساسيّ وهو النوّاب Delegates. حيث اكتشفنا كيف أنّ النوّاب هي وسائل لتحقيق مبدأ الردود callbacks المهم في عالم البرمجة، والمستخدم على نطاق واسع في أنظمة التشغيل في تبادل الرسائل بين الكائنات المختلفة. تعلّمنا كيف يُغلّف النائب تابعًا يوافق معايير معيّنة تكون محدّدة عند التصريح عن النائب. في الحقيقة تُعتبر النوّاب الركن الأساسيّ للأحداث Events، ذلك المفهوم المهم في البرمجة كائنيّة التوجّه، والذي سنتحدّث عنه في الدرس التالي.
-
يوفّر Microsoft Outlook خاصية تفويض إدارة البريد الإلكتروني إلى شخص آخر يُدعى Delegate (المُفوَّض أو الممثل). حيث يمكنك أن تسمح لذلك الشخص باستقبال وإرسال البريد الإلكتروني وطلبات الاجتماعات وإدارة المجلدات بالنيابة عنك. نستطيع تشبيه الأمر بالمدير الذي يفوّض إدارة بريده وجدول أعماله إلى مساعده. ملاحظة: تتطلّب هذه الخاصية حساب Microsoft Exchange ويجب أن يستخدم كلا من المفوِّض والمفوَّض له Outlook. لتخويل أحد ما إدارة بريدك الإلكتروني اذهب إلى: File > Account Settings > Delegate Access انقر على زر Add لإضافة جهة الاتصال كمفوَّض: من مربّع الحوار Add User حدّد المستخدم/المستخدمين الذين تريد تفويض إدارة بريدك الإلكتروني إليهم، وانقر على Add ثم OK: سيظهر بعد ذلك مربع الحوار Delegate Permission والذي يمكنك من خلاله تحديد مستوى الوصول/التخويل الذي تمنحه للمفوَّض. تتوفر أربعة مستويات للتخويل: None: المفوّض له لا يستطيع اتخاذ أي إجراء على مجلداتك (البريد، جهات الاتصال، التقاويم...إلخ). Reviewer: يستطيع المفوّض له عرض العناصر في مجلداتك فقط. Author: يستطيع المفوّض له عرض العناصر وإضافتها. على سبيل المثال يمكنه إنشاء موعد على التقويم أو إضافة مهمة. كما يستطيع حذف أو تعديل العناصر التي قام هو بإضافتها. Editor: يستطيع المفوّض له القيام بجميع إجراءات المستوى السابق (Author) كما يستطيع تعديل وحذف العناصر التي قمت أنت بإضافتها. اختر المستوى المرغوب لكل مجلد من المجلدات، ثم حدّد الخيار Automatically send a message to delegate summarizing these permissions إذا كنت تريد إرسال رسالة إلى المفوّض له تلخّص جميع الأذونات التي منحتها له. وإذا رغبت في منحه الإذن لعرض العناصر الخاصة Private قم بتأشير الخيار Delegate can see my private items. بعد تخصيص خياراتك قم بالنقر على OK. وإذا قمت بتأشير الخيار الأول المذكور أعلاه، ستصل رسالة إلى المفوّض تطلعه على جميع الأذونات التي حصل عليها. يمكنك إدارة المفوّضين لهم (حذف، إضافة، تعديل الأذونات) بالذهاب إلى: File > Account Settings > Delegate Access فإذا رغبت في حذف مفوّض له حدّده من مربّع الحوار Delegates ثم انقر على زر Delete: يمكنك أيضًا الوصول إلى الأذونات الخاصة بكل مجلد (كمجلد على البريد Mail، على التقاويم Calendar، إلخ) بالنقر بزر الفأرة الأيمن على المجلد ثم اختيار Properties: ستجد في تبويب Permissions أسماء الأشخاص الذين قمت بتفويض إدارة مجلداتك إليهم. ومن قائمة Permission Level يمكنك الوصول إلى مستويات التخويل القياسية التي ذكرناها أعلاه بالإضافة إلى مستويات إضافية. وتحت كل مستوى تقوم بتحديده هناك مجموعة خيارات أخرى يمكنك التحكّم بها: إذا رغبت في تغيير التخويل الذي منحته لأحد المفوّضين لهم، قم بتحديد اسمه ثم غيّر المستوى من قائمة Permission Level وخصص الخيارات الخاصة بالمستوى الذي حددته ثم انقر على OK.
-
- بريد إلكتروني
- تفويض
-
(و 2 أكثر)
موسوم في: