رویدادها در #C – به زبان ساده


همه ما معنی لفظی واژه «رویداد» را میدانیم. رویداد چیزی است که قرار است اتفاق بیفتد و یا اتفاق افتاده است. معنی رویداد (Event) در زبان برنامهنویسی سی شارپ نیز جز این نیست. از لحاظ مفاهیم برنامهنویسی، رویداد به معنی شیئی است که یک رویداد تولید کرده است و شیء یا اشیای دیگر باید آن رویداد را مدیریت کنند. شیء (ناشر) که رویداد را تولید میکند در واقع به اشیای دیگر (مشترکان) اطلاع میدهد که چیزی رخ داده است و آن اشیا (مشترک) باید کاری در خصوص آن رویداد انجام دهند (مدیریت کنند). این رفتار بین اشیا به طور عمده در الگوی طراحی نرمافزار observer (مدل ناشر-مشترک) دیده میشود. ما در این مقاله تلاش میکنیم مفهوم رویدادها در #C را به زبانی ساده توضیح دهیم.
Delegate
از آنجا که رویدادها (Events) و وکالتها (Delegates) به طور جداییناپذیری با یکدیگر پیوسته هستند، لازم است که ابتدا با طرز کار وکالتها آشنا شویم. منظور از delegate در زبان سی شارپ یک نوع شیء است. این نوع از شیء میتواند به عنوان یک متغیر مورد استفاده قرار گیرد. اما یک متغیر delegate به یک مقدار اشاره نمیکند، بلکه به یک متد اشاره دارد. از این رو شیء delegate تنها میتواند به متدهایی اشاره کند که امضای آن با delegate تطبیق پیدا کند.
ساختار اعلان یک delegate به صورت زیر است:
access_specifier delegate retun_type delegateName(parameter_list);
به مثال زیر توجه کنید:
public delegate int CalculateDelegate(int a, int b);
متدهای زیر با delegate به نام CalculateDelegate تطبیق مییابند:
public int Sum(int a, int b) { return a + b; } public int Subtract(int a, int b) { return a - b; } public int Average(int a, int b) { return (a + b) / 2; } public int Multiply(int a, int b) { return a * b; } public int Divide(int a, int b) { return a / b; }
ما با استفاده از multicasting میتوانیم این متدها را با هم ترکیب کرده و آنها را در یک شیء CalculateDelegate نگهداریم. سپس با استفاده از این شیء میتوانیم همه متدها را اجرا کنیم.
برای اجرای (اجرا یا فراخوانی) همه متدها با استفاده از شیء calc میتوانید از InvocationList مربوط به delegate استفاده کنید.
واریانس در Delegate
زمانی که متدی را به delegate انتساب میدهیم، امضای متد لزومی ندارد دقیقاً با delegate تطبیق پیدا کند. این مسئله کوواریانس و کنتراواریانس نامیده میشد. کوواریانس روی نوع بازگشتی متد اعمال میشود، در حالی که کنتراواریانس روی نوع پارامتر متد اعمال خواهد شد. در ادامه مثالی از کوواریانس را میبینید:
در ادامه مثالی از یک کنتراواریانس را ملاحظه میکنید:
Delegate-های داخلی
#C برخی delegate-های داخلی را عرضه کرده است که برای مقاصد رایج مختلف، مفید هستند. این delegate-ها یک نمادگذاری اختصاری ارائه میکنند که تقریباً نیاز به اعلان کردن انواع delegate را رفع میکند. فهرست آنها به شرح زیر است:
- Action - این نماد به همراه متدهایی استفاده میشود که مقداری بازگشت نمیدهند و هیچ فهرست پارامتری ندارند.
- <>Action - به همراه متدهایی استفاده میشود که دست کم یک آرگومان دارند و مقداری بازگشت نمیدهند.
- <>Func - به همراه متدهایی استفاده میشود که مقداری بازگشت میدهند و دارای فهرست پارامتر هستند.
- <>Predicate - این نماد نشاندهنده متدی است که یک پارامتر ورودی میگیرد و یک مقدار بولی بر مبنای برخی معیارها بازگشت میدهد.
این مقدار توضیحات در خصوص delegate-ها کافی است. مطالب آنلاین زیادی وجود دارند که میتوانید با بهرهگیری از آنها با delegate-ها آشنا شوید. اما تمرکز این مقاله روی توضیح استفاده از delegate-ها در یک رویداد است.
مدیریت و تولید یک رویداد در C#
زمانی که یک delegate اعلان شد، هرکس میتواند ارجاع متد آن را با استفاده از نماد تساوی (=) بازنویسی کند. فرض کنید یک شیء delegate دارید که چند ارجاع متد در خود جای داده است. اگر کسی از = برای اشاره به یک متد جدید استفاده کند در این صورت همه ارجاعهای دیگر متد نیز از دست خواهند رفت. از سوی دیگر Event اقدام به کپسولهسازی یک delegate میکند و بدین ترتیب جلوی بازنویسی یک ارجاع متد از طریق محدودسازی استفاده از عملگر انتساب (=) گرفته میشود.
مشکل دیگر این است که یک delegate میتواند خارج از یک کلاس فراخوانی (در واقع از هر جایی) فراخوانی شود. Event این مشکل را نیز حل میکند، زیرا رویداد نمیتواند خارج از کلاس فراخوانی شود. بدین ترتیب مطمئن میشویم که رویداد تنها از سوی کدی فراخوانی میشود که بخشی از کلاس (ناشر) است و رویدادی را که تعریف کرده در صورت برقرار شدن شرایط خاص اجرا میکند. همچنین یک رویداد همواره یک عضو دادهای از یک کلاس یا struct است و میتواند درون یک متد اعلان شود.
فرض کنید میخواهیم زمانی که فرد روی دکمهای روی گوشی میزند یا زمانی که دما از سطح معینی فراتر میرود، اتفاقات زیر بیفتند:
- تهویه هوا روشن شود.
- درها/پنجرهها بسته شوند.
- یک بستنی ارزان شکلاتی-نعنایی سفارش داده شود.
این سه کار که باید اتفاق بیفتند، اکشن نام دارند و از سوی یک شیء delegate در زمان رخ دادن رویداد وکالت میشوند. بنابراین دو رویداد مختلف وجود دارند که یک delegate را برای اجرای برخی متدها تریگر میکنند.
اکنون که دو تریگر رویداد یعنی موبایل و دماسنج داریم، میتوانیم از یک اینترفیس استفاده کنیم، زیرا هر دوی آنها کد یکسانی دارند. بنابراین کار خود را با اینترفیس اول آغاز میکنیم:
توجه کنید که ما از delegate ژنریک NET. به نام EventHandler<> استفاده میکنیم و آرگومانهای سفارشی خاص خود به نام EventArgs را ارسال مینماییم. کلاس EventHandler<> سفارشی به صورت زیر است:
TemperatureEventArgs از کلاس .NET EventArgs ارثبری کرده است و تنها یک بخش از اطلاعات یعنی مقدار دما را نگهداری میکند در ادامه کلاس مشترک را نیز ایجاد میکنیم که رویداد .NET EventArgs را پیادهسازی خواهد کرد:
کلاس mobile یک تریگر دیگر است که کد یکسانی با کلاس thermometer دارد. کلاس thermometer و کلاس Mobile دارای یک فیلد خصوصی هستند که مقدار بالاترین دما را نگهداری میکند و از طریق سازنده تعیین میشود. هر زمان که دما از مقدار بالاترین دما فراتر رود، کلاس thermometer اقدام به فراخوانی delegate میکند:
OnTemperatureChanged(this, temperatureValue);
در این زمان delegate به نام OnTemperatureChanged مسئولیت اجرای اکشنهای subscriber را بر عهده دارد. شاید بپرسید که مشترک از کجا میداند متدها اجرا خواهند شد؟ ناشر نه تنها به مشترک اطلاع میدهد، بلکه اجرای متدهای مشترک را نیز از طریق یک delegate به نام OnTemperatureChanged تریگر میکند.
کلیدواژه this به کنترل/تریگری اشاره دارد که delegate را اجرا کرده است و در این مورد thermometer است. پارامتر temperatureValue مقدار دمای کنونی است و از طریق آرگومان رویداد به مشترک یعنی اپلیکیشن یا دستگاهی که قرار است اکشنها یا متدهای نگهداری شده از سوی delegate را اجرا کند، ارسال میشود. مشترکی که به تریگرها گوش میدهد تا برخی اکشنها را اجرا کند به صورت زیر است:
تریگر (موبایل/دماسنج) ایجاد و بالاترین مقدار دما به صورت زیر تعیین شده است:
IEventTrigger trigger = new Thermometer(30);
برخی اکشنها به یک delegate با نام OnTemperatureChanged اضافه شدهاند. زمانی که دما (خارج از ناشر و مشترک) تغییر یابد، set accessor در تریگر اجرا میشود. متد set accessor بررسی میکند آیا دمای کنونی بالاتر از سطح بیشینه دما رسیده است یا نه. اگر چنین باشد، یک delegate با نام OnTemperatureChanged در شیء ناشر اجرا میشود. این delegate نماینده delegate روی شیء تریگر یعنی trigger.OnTemperatureChanged است. بنابراین زمانی که delegate در متد set accessor اجرا شود، همه متدهای ارجاعیافته/الصاقیافته فراخوانی خواهند شد.
سخن پایانی
در این مقاله تنها از یک مشترک استفاده شده است، اما شما میتوانید چندین مشترک داشته باشید که در یک یا چند اشتراک ثبت نام کنند. اشتراکها از سوی ناشر منتشر میشوند. مشترک از += برای ثبت نام در رویداد (های) یک ناشر استفاده میکند. این عملگر به ناشر اعلام/درخواست میکند که «این اکشنها را در زمانی که شرط برقرار شد اجرا کن». شرط از سوی ناشر تعریف میشود. اکشنها از سوی مشترکان تعریف میشوند و هر مشترک میتواند اکشنهای متفاوتی برای اجرا در زمان وقوع یک رویداد داشته باشد. امیدواریم مطالعه این مقاله برای شما مفید بوده باشد و توانسته باشید در خصوص رویدادها و delegate-ها در سی شارپ اطلاعاتی کسب کنید.
با سلام، قسمت اسکرول بار قسمت برچسب ها، مشکل فنی داره.
با سلام؛
از بازخورد شما بسیار سپاسگزاریم. اگر نوع مرورگر، سیستم عامل و دستگاه مورد استفاده خودتون رو به همراه مشکل اطلاع بدین، این مشکل را سادهتر بررسی و برطرف میکنیم.
با تشکر از همراهی شما با مجله فرادرس
با سلام.
در قطعه کد:
access_specifier delegate retun_type delegateName(parameter_list)
فکر میکنم سمی کالن لازم باشه.
با تشکر از زحمات زیادتون، لطفاً مثال های عملی از دلیگیت ها با موضوع رسم اشکال هندسی و عملیات محاسبات ریاضی بزنید.
چرا که بنده این مثال های پیچیده، برایم قابل فهم نیست و فقط مثال از اشکال هندسی را می فهمم.
باز هم ممنون
با سلام و احترام؛
صمیمانه از همراهی شما با مجله فرادرس و ارائه بازخورد سپاسگزاریم.
این مورد اصلاح شد.
حتماً نهایت سعی خود را خواهیم داشت تا در صورت امکان در آینده مطلب دیگری را با این موضوع در مجله فرادرس منتشر کنیم و در آن مثالهای سادهتری را ارائه دهیم. البته بخش هفتم دوره آموزشی زیر به موضوع Delegate اختصاص دارد که در صورت تمایل میتوانید فیلم آموزشی مربوط به این بخش را مشاهده کنید.
برای شما آرزوی سلامتی و موفقیت داریم.
سلام
در قسمت Delegate جمله “اما یک متغیر delegate به یک مقدار اشاره میکند، بلکه به یک متد اشاره دارد.” چک شود
با تشکر
سلام و وقت بخیر؛
مورد مربوطه اصلاح شد.
از توجه و همراهی شما با مجله فرادرس متشکریم.