الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

Delegate in .Net , Part 3

بدأه AliBazzi في 21 سبتمبر 2010 · 0 رد · 1,103 مشاهدة · في قسم تكنولوجيا Microsoft .NET العام
مشاركة: واتساب X فيسبوك تيليجرام
#1

قبل إكمال القراءة , يرجى فهم محتويات المقال الأول و الثاني الموجودان في الروباط التالية على الترتيب

/index.php?showtopic=229074

/index.php?showtopic=229075

أي Delegate في الـ Net. هو Type وتحديدا Class مشتق مباشرة من الـ System.MuliCastDelegate الذي بدوره يرث من System.Delegate , فلو قمنا بعمل Reflection لبرنامج يحتوي السطور التالية :

delegate void Do(string str);
    class Program
 	{
        static void Main(string[] args)
        {
     		Do del = new Do(foo);
                }
        static void  foo(string str) { Console.WriteLine(str); }
    }

سنجد عند إستعمال برنامج Reflector

Capture.PNG

ونلاحظ مما سبق , بأن الـ Do ما هو إلا Class يقوم بالوراثة فعليا من MultiCastDelegate ولا يكتفي بذلك , بل يقوم بإضافة عدة Methods كـ Members ...

ولكن أي Compiler بعاملته معاملة خاصة , لا أكثر ... فسيمنعك أي Compiler تنتجة مايكروسوفت من الوراثة بشكل مباشر من MultiCastDelegate , فلن تستطيع عمل شيء كالتالي في #Compiler C أو غيرة من الـ Compilers المنتجة من قبل مايكروسوفت :

class  MyClass :  System.MulticastDelegate
    {
        //...
    }

هذا الخيار من قِبَلهم هو للحماية ليس أكثر ...

ما يهمنا من التعرف على تفاصيل الـ Delegate هو ليس فقط , لكي نطلع على أصله أو بنيته التحتية , بل هذين الـصنفين MultiCastDelegate , Delegate يحويان بالتأكيد ما يفيد و يستحق الإسكتشاف (وإلا لما قام أي Delegate وبالوراثة منهم ؟) , فسأعرض في السطور القادمة خصائص أي Delegate خاضع لبيئة الـ Net.

أي Delegate هو Immutable (ولمن لا يعرف معنى Immutable توجه إلى: /index.php?showtopic=216966&st=0&p=1073741) , فعند تنفيذ أي عملية من العمليات التالية عليه , سيحافظ الـ Object الأصلي على حالته , عوضا عن ذلك , ستقوم أي عملية أو Method بإعادة Delegate Object جديد تمام , يحوي التعديلات المرجوة ...

فأول خصائص الـ Delegate هي قابليته لحمل عدة Method Pointers في نفس الوقت , فحتى الاّن كل الأمثلة التي طرحت , يقوم الـ Delegate فيها بحمل مؤشر إلى Method وحيدة , ولكن بإمكاننا أن نحمّل عدة Methods في Delegate Object وحيد كالتالي :

class Program
    {
        static void  Main(string[] args)
        {
            Action del = new  Action(() => Console.WriteLine("hi"));
            del += new  Action(delegate() { Console.WriteLine("Man"); });
            del +=  () => { Console.WriteLine("How"); };
            del += () =>  Console.WriteLine("are");
            del += () =>  Console.WriteLine("you");
            del.Invoke();
        }
 	}

فعندما نقوم بطلب Invoke على Del سيقوم الـ Delegate بتنفيذ الخمس Methods السابقات (واحدة anonymous والباقي Lambda Expressions ) بترتيب إضافتهم عن طريق الـ =+ Operator ,الموجود في صلب الـ #C كلغة ...وسيكون الناتج كما تتوقع :

hi

Man

How

are

you

ولكن تحت الكواليس , يقوم الـ #Compiler C بطلب Static Delegate.Combine(delegate,Delegate) Method التي تتطلب الـ Delegate القديم , مع الجديد المراد إضافته , لكي تدمج في ما بينهم , ولكنها لا تعدّل القديم , بل تعيد Delegate Object جديد يحوي مجموع ما مرر إليها , بنفس ترتيب الإضافة ...

فالبرنامج التالي مكافئ لما تم عرضه قبل قليل :

class  Program
    {
        static void Main(string[] args)
        {
     		Action del = new Action(() => Console.WriteLine("hi"));
     		del = (Action) Delegate.Combine( del , new Action(delegate() {  Console.WriteLine("Man"); }));
            del = (Action)  Delegate.Combine( del , new Action(() => { Console.WriteLine("How");  }));
            del = (Action) Delegate.Combine( del , new Action(()  => Console.WriteLine("are")));
            del = (Action)  Delegate.Combine( del , new Action(() => Console.WriteLine("you")));
     		del.Invoke();
        }
    }

فالمثال السابق يقوم بجمع كل ما يحويه الـ Delegate القديم (حتى لو أحتوى على أكثر من مؤشر لـ Method ) مع الـ Method الجديدة المراد إضافتها , ولكن يرجى الإنتباه إلى عدم نسيان إسناد النتيجة الجديدة المعادة من Delegate.Combine إلى الـ del نفسه , ليتم إستعمالها لاحقا ...

يمكن الوصول في أي لحظة إلى الـ Methods الذي يقوم الـ Delegate بالتأشير عليها عن طريق Del.GetInocationList() Method الموروثة من MultiCastDelegate والتي تعيد Array تحوي Delegate يمثل كل Method يحملها الـ Delegate الأصلي ... فيمكننا عمل التالي :

class Program
    {
        static void  Main(string[] args)
        {
            Action del = new  Action(() => Console.WriteLine("hi"));
            del = (Action)  Delegate.Combine( del , new Action(delegate() {  Console.WriteLine("Man"); }));
            del = (Action)  Delegate.Combine( del , new Action(() => { Console.WriteLine("How");  }));
            del = (Action) Delegate.Combine( del , new Action(()  => Console.WriteLine("are")));
            del = (Action)  Delegate.Combine( del , new Action(() => Console.WriteLine("you")));

     		foreach (var method in del.GetInvocationList())
     		{
                (method as Action).Invoke();
            }
 		}
    }

ما قمنا بعمله في هذا المثال بصراحة لا يتعدا كونه إعادة تكرار لما تقوم به Invoke إذا ما طلبناها على Del ...فالنتيجة ستكون مشابهة للمثال الأصلي

وكما نستطيع إضافة Method ,نستطيغ بالتأكيد حذفها من Delegate ايضا , فلنأخذ المثال التالي :

class Program
    {
        private static  void foo()
        {
            Console.WriteLine("How are you  ?");
        }

        static void Main(string[] args)
 		{
            Action del = new Action(() =>  Console.WriteLine("hi"));
            del += new Action(delegate() {  Console.WriteLine("Man"); });
            del += foo;
     		del.Invoke();

            Console.WriteLine("After Removing");

     		del -= new Action(delegate() { Console.WriteLine("Man"); });
     		del -= foo;
            del.Invoke();
        }
    }

وستكون النتيجة غير متوقعة للوهلة الأولى:

hi

Man

How are you ?

After Removing

hi

Man

لأننا قمنا بحذف الـ Delegate التالي :

del -= new Action(delegate() {  Console.WriteLine("Man"); });

ولكن ذلك , لم يؤثر شيء على النتيجة , فيبدو أنه لم يستطع حذفها (وهذا ما حصل فعلا , لأنه لم يكن يجده بالأساس لحذفه) , والسبب يمكن توضيحه بالثلاث أسطر التالية :

Action del1 = new Action(() =>  Console.WriteLine("Hi"));
            Action del2 = new Action(()  => Console.WriteLine("Hi"));
            Console.WriteLine(del1 ==  del2); // Will print false ...

والسبب في ذلك , أنك عندما تقوم بتعريف Delegates يحويان Anonymous method أو Lambda Expression متكافئين في العمل , لايؤدي بالضرورة إلى تساوي الـ Delegates المُنشأة !

لأن كل Lambda Expression هو Method جديدة حتى لو تكافئت مع Lambda Expression معرفة مسبقا ...

ولكن , لم يكن الحال كذلك مع foo الذي إستطاعنا حذفها بنجاح ...

إذا المشكلة في تعريف كل من Lambda Expressions و Anonymous Methods (فـ C# Compiler لا يقوم بإختبار الـ Equivalence بين الـ Lambda Expressions أو Anonymous Methods ), و ليس في الـ Delegate نفسه ...

ولنكن أكثر وضوحا , فإن أي Lambda Expression أو Anonymous Methods تأوّل في النهاية إلى Method طبيعية , يختار الـ Compiler إسمها بشكل عشوائي , فالبرنامج التالي :

class Program
    {
        private  static void foo()
        {
            Console.WriteLine("How are  you ?");
        }

        static void Main(string[] args)
 		{
            Action del = new Action(foo);
            del  += () => Console.WriteLine("bye");
            del += () =>  Console.WriteLine("!");
            del.Invoke();
        }
 	}

ما هو إلا :

Capture1.PNG

كما يظهر في الشكل السابق , فقد قام الـ Compiler بإضافة كل من

<main>b_0 ()
<main>b_1()

ليمثلا الـ Lambda expressions الموجودين مسبقا في المثال ... (أكره هذه اللحظات , التي يكشف فيها جمال الأشياء , أتمى أن لا تفقد Lambda Expression بريقها عندك icon_e_wink.gif )

فهذا هو السبب الرئيسي في وقوع المشكلة السابقة عند الحذف , فالـ Compiler سيقوم بتوليد Method جديدة لكل Lambda Expression أو Anonymous Method يصادفها , دون إختبار إحتمال وجودهما مسبقا.

وكما هو حال Combine في أنها مكافئ =+ , فما يكافئ =- هو Delegate.Remove , فالبرنامج التالي يكافئ السابق :

class Program
    {
        private static  void foo()
        {
            Console.WriteLine("How are you  ?");
        }

        static void Main(string[] args)
 		{
            Action del = new Action(() =>  Console.WriteLine("hi"));
            del += foo;
     		del.Invoke();

            Console.WriteLine("After Removing...");

     		del =(Action)Delegate.Remove(del,new Action(foo));
     		del.Invoke();
        }
    }

بقي لدينا Combine All التي تقوم بضم عدة Delegate وجمعهم في Delegate وحيد كباقة :

class Program
    {
        private static  void foo()
        {
            Console.WriteLine("How are you  ?");
        }

        static void Main(string[] args)
 		{
            Action del = (Action)Delegate.Combine
         		(new Action(foo),
                new  Action(()=>Console.WriteLine("Hi!")),
                new  Action(()=>{Console.WriteLine();}),
                new  Action(delegate { Console.WriteLine("^_^"); })
                );
     		del.Invoke();
        }
    }

لم نغطي كل ميزات الـ Delegate فنطاق عمله أوسع مما نتخيل , ربما يتم في المستقبل القريب إن شاء الله مناقشة Begin Invoke و End Invoke اللتين تتعلقان بشكل مباشر بالـ Asynchronous Pattern in .Net ...

إنتهت هذه السلسة من الدروس عن الـ Delegate وحان وقت الأسئلة ... icon_e_wink.gif

تم تعديل هذه المشاركة بواسطة AliBazzi في 21 سبتمبر 2010 في 18:56

مواضيع مشابهة