رخدادها و delegate ها در C# - قسمت دوم
اعلان Delegate
در مثال ما، متد Tick از واسط Tickable از نوع void بود و هيچ پارامتري دريافت نميكرد :
كد: |
interface Tickable |
براي اين متد ميتوان delegate ي تعريف نمود كه ويژگيهاي آنرا داشته باشد :
كد: |
delegate void Tick(); |
همانطور كه قبلاً نيز گفته شد، اين عمل نوع جديدي را ايجاد مينمايد كه ميتوان از آن همانند ساير انواع استفاده نمود. مثلاً ميتوان آنرا بعنوان پارامتري براي يك متد در نظر گرفت :
كد: |
void Example(Tick param) |
فراخواني delegate
قدرت و توانايي delegate زماني مشهود ميگردد كه ميخواهيد از آن استفاده نماييد. براي مثال، با متغير paramدر مثال قبل چكار ميتوانيد انجام دهيد؟ اگر param متغيري از نوع int بود، از مقدار آن استفاده ميكرديد و با استفاده از عملگرهايي نظير +، - و يا عملگرهاي مقايسهاي، عملي خاص را بر روي آن انجام ميداديد. اما حال كه param متغيري از نوع int نيست، چه ميكنيد؟ متغير param يك delegate است و همانطور كه گفته شد، delegateانتزاعي از يك متد است، پس هر عملي كه متد انجام ميدهد، delegate نيز ميتواند انجام دهد. با استفاده از پرانتز، ميتوان از delegate استفاده نمود :
كد: |
void Example(Tick param) |
نكته : همانطور كه اشاره شد، delegate يكي از انواع مرجعي است از اينرو مقدار آن ميتواند برابر با Null باشد. در مثال فوق، اگر مقدار param برابر با Null باشد، كامپايلر خطاي NullReferenceException را ايجاد مينمايد.
همانند متدها، delegate ها بايد بطور كامل و صحيح فراخواني گردند. با توجه به اعلان Tick، در زمان فراخواني اينdelegate، مثلاً param، بايد توجه داشت كه هيچ پارامتري را نميتوان به آن ارسال نمود و نميتوان آنرا به متغيرينسبت داد چراكه اين delegate بصورت void اعلان شده و مقدار بازگشتي ندارد.
كد: |
void Example(Tick param) |
توجه نماييد كه delegate را به هر نحوي ميتوانيد اعلان نماييد. براي مثال به نسخة ديگري از Tick توجه كنيد :
كد: |
delegate void Tick(int hours, int minutes, int seconds); |
اما به ياد داشته باشيد كه همانند متد، در هنگام استفاده از آن بايد پارامترهاي صحيح به آن ارسال نماييد :
كد: |
void Example(Tick method) |
با استفاده از delegate ميتوانيد كلاس Ticker را پيادهسازي كنيد :
كد: |
delegate void Tick(int hours, int minutes, int seconds); |
ساخت نمونههاي جديد از يك delegate
آخرين كاري كه بايد انجام دهيد، ايجاد نمونههاي جديد از delegate ساخته شده است. يك نمونة جديد از يكdelegate، تنها انتزاعي از يك متد است كه با نامگذاري آن متد ايجاد ميشود.
كد: |
class Clock |
با توجه به ساختار Tick، ملاحظه مينماييد كه متد RefreshTime كاملاً با اين delegate همخواني دارد :
كد: |
delegate void Tick(int hours, int minutes, int seconds); |
و اين بدين معناست كه ميتوان نمونة جديد از Tick ايجاد كرد كه انتزاعي از فراخواني RefreshTime در شيء خاصي از Clock است.
كد: |
Clock wall = new Clock(); ⋮ |
حال كه m، ايجاد شد، ميتوانيد از آن بصورت زير استفاده نماييد :
كد: |
m(12, 29, 59); |
اين دستور در حقيقت كار دستور زير را انجام ميدهد (چون m دقيقاً انتزاع آن است) :
كد: |
wall.RefreshTime(12, 29, 59); |
همچنين ميتوانيد m را بعنوان پارامتر به متدي ارسال نماييد. حال تمام چيزهايي را كه براي حل مسئله با استفاده از delegate بدانها نياز داشتيم را بررسي كرديم. در زير مثالي را مشاهده ميكنيد كه كلاسهاي Ticker و Clock را به يكديگر مرتبط نموده است. در اين مثال از واسط استفاده نشده و متد RefreshTime، متدي private است :
كد: |
delegate void Tick(int hours, int minutes, int seconds); ⋮ |
با اندكي تامل و صرف وقت ميتوانيد delegate را بطور كامل درك نماييد.
رخدادها (Events)
در برنامههاي Console ، برنامه منتظر ورود اطلاعات يا دستوراتي از سوي كاربر ميماند و با استفاده از ايناطلاعات كار مورد نظر را انجام ميدهند. اين روش برقراري ارتباط با كاربر، روشي ناپايدار و غير قابل انعطاف است. در مقابل برنامههاي Console، برنامههاي مدرن وجود دارند كه با استفاده از GUI با كاربر در ارتباطند و بر پايه رخدادها بنا شدهاند (Event-Based)، بدين معنا كه رخدادي (منظور از رخداد اتفاقي است كه در سيستم يا محيط برنامه صورت ميگيرد.) در سيستم روي ميدهد و بر اساس اين رخداد عملي در سيستم انجام ميشود. در برنامههاي تحت ويندوز، نيازي به استفاده از حلقههاي متعدد جهت منتظر ماندن براي ورودي از كاربر نيست، بلكه با استفاده از رخدادها، تراكنش بين سيستم و كاربر كنترل ميشود.
يك event در زبان C#، عضوي از كلاس است، كه در صورت بروز رخداد خاصي، فعال ميشود و عملي را انجام ميدهد. معمولاً براي فعال شده event از دو عبارت fires و raised استفاده ميشود. هر متدي كه بخواهد، ميتواند در ليست رخداد ثبت شده و به محض اتفاق افتادن آن رخداد، از آن مطلع گردد.
بطور كلي ميتوان گفت كه يك رخداد همانند يك فيلد اعلان ميشود با اين تفاوت مهم كه نوع آنها حتماٌ بايد يكdelegate باشد.
Delegate و رخدادها در كنار يكديگر كار ميكنند تا قابليتهاي يك برنامه را افزايش دهند. اين پروسه با شروع يك كلاس كه يك رخداد را تعريف ميكند، آغاز ميشود. هر كلاسي، كه اين رخداد را درون خود داشته باشد، در آن رخداد ثبت شده است و ميتواند متدي را به آن رخداد تخصيص دهد. اين عمل با استفاده از delegate ها صورت ميپذيرد، بدين معني كه delegate متدي را كه براي رخداد ثبت ميشود را تعيين مينمايد. Delegate ها ميتوانند هر يك از delegate هاي از پيش تعريف شدة .Net و يا هر delegate ي باشند كه توسط كاربر تعريف شده است. بطور كلي، delegate ي را به رخدادي تخصيص ميدهيم تا متدي را كه بهنگام روي دادن رخداد فراخواني ميشود، معين گردد. مثال زير روش تعريف رخداد را نشان ميدهد.
مثال 2-14 : اعلان و پيادهسازي رخدادها
كد: |
using System; |
در اين مثال، ابتدا اعلان يك delegate ديده ميشود. درون كلاس، رخدادي با نام MyEvent و از نوع MyDelegateتعريف شده است. در متد Main() نيز مرجع جديدي به رخداد MyEvent افزوده شده است. همانطور كه در اين مثالنيز مشاهده ميكنيد، delegate ها تنها با استفاده از += ميتوانند به رخدادها افزوده شوند. در اين مثال هر گاهMyEvent فراخواني شود، متد CallbackMethod اجرا ميشود چراكه با استفاده از مرجع delegate به رخداد مرتبط شده است. (يا در اصطلاح در رخداد ثبت شده است.)
مثال فوق را بدون استفاده از رخداد نيز ميتوان نوشت. اين نسخه از مثال 2-14 كه تنها در آن از delegate استفاده شده در زير آورده شده است :
كد: |
using System; |
بايد توجه كنيد كه موارد كاربرد رخدادها بيشتر در برنامههاي تحت ويندوز نمايان ميشود و در اينجا شايد وجود آنها در برنامه براي شما مشهود نباشد. در آينده، به بررسي برنامهنويسي فرمهاي ويندوز نيز خواهيم رسيد و در آنجا به طور مفصل درباره event ها و delegate ها مجدداً بحث خواهيم نمود.
بطور خلاصه ميتوان گفت، با استفاده از delegate ها روشي براي ايجاد دسترسي به متدها بط.ور پويا را فراهم نموديم. با استفاده از رخدادها نيز، در صورت بروز اتفاقي خاص، عملي خاص انجام ميگيرد. اين عمل معمولاٌ با استفاده از يك delegate كه مرجعي به يك متد در خود دارد انجام ميگيرد.
توضيحات پيشرفته :
در انتهاي اين درس ميخواهم توضيحات پيشرفته تري را نيز در اختيار شما قرار دهم. در قسمت مربوط به delegateها در همين درس، مثالي مطرح شد كه در آن delegate ي با نام Tick وجود داشت. اعلان اين delegate به صورت زير بود :
كد: |
delegate void Tick(int hours, int minutes, int seconds); |
|
حال ميخواهيم به اين مثال يك رخداد نيز اضافه كنيم. در زير رخداد tick از نوع Tick اعلان ؼ/div>
انجام پروژه های برنامه نویسی