مباحث تکمیلی رخدادها (Events) :
رخداد مکانیزمی است که توسط آن یک کلاس می تواند کلاینت های خودش را از اتفاق افتادن امری باخبر سازد. رخدادها توسط Delegates تعریف می شوند.
برای توضیحات بیشتر بهتر است در ابتدا یک برنامه ی کامل را ملاحظه نمود . سپس قسمت به قسمت آن آنالیز خواهد گشت:
کد:
using System;
public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);
public class DivBySevenEventArgs : EventArgs
{
public readonly int TheNumber;
public DivBySevenEventArgs(int num)
{
TheNumber = num;
}
}
public class DivBySevenListener
{
public void ShowOnScreen(object o, DivBySevenEventArgs e)
{
Console.WriteLine(
"divisible by seven event raised!!! the guilty party is {0}",
e.TheNumber);
}
}
public class BusterBoy
{
public static event DivBySevenHandler EventSeven;
public static void Main()
{
DivBySevenListener dbsl = new DivBySevenListener();
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
GenNumbers();
}
public static void OnEventSeven(DivBySevenEventArgs e)
{
if(EventSeven!=null)
EventSeven(new object(),e);
}
public static void GenNumbers()
{
for(int i=0;i<99;i++)
{
if(i%7==0)
{
DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);
OnEventSeven(e1);
}
}
}
}
OUTPUT
F:\C#\events>csc 1.cs
Microsoft (R) Visual C# Compiler Version 7.00.9254 [CLR version v1.0.2914]
Copyright (C) Microsoft Corp 2000-2001. All rights reserved.
F:\C#\events>1
divisible by seven event raised!!! the guilty party is 0
divisible by seven event raised!!! the guilty party is 7
divisible by seven event raised!!! the guilty party is 14
divisible by seven event raised!!! the guilty party is 21
divisible by seven event raised!!! the guilty party is 28
divisible by seven event raised!!! the guilty party is 35
divisible by seven event raised!!! the guilty party is 42
divisible by seven event raised!!! the guilty party is 49
divisible by seven event raised!!! the guilty party is 56
divisible by seven event raised!!! the guilty party is 63
divisible by seven event raised!!! the guilty party is 70
divisible by seven event raised!!! the guilty party is 77
divisible by seven event raised!!! the guilty party is 84
divisible by seven event raised!!! the guilty party is 91
divisible by seven event raised!!! the guilty party is 98
F:\C#\events>
توضیحاتی در مورد کد فوق:
در کد فوق هرگاه عددی تولید شود که بر 7 قابل قسمت است ، رخدادی صادر می گردد. رویداد گردان (event handler) در این حالت پیغامی را مبتنی بر اتفاق افتادن رخداد بر روی صفحه به همراه عدد مربوطه نمایش می دهد.
اولین کاری که برای این منظور انجام شد تعریف یک delegate است :
کد:
public delegate void DivBySevenHandler(object o, DivBySevenEventArgs e);
این delegate پارامترهایی را که باید به رویداد گردان فرستاد ، تعریف می نماید. بنابراین هر کلاسی که بخواهد این رخداد را اداره نماید باید متدی تعریف کند که دقیقا آرگومانهای ورودی و خروجی آن همانند این delegate باشد.
همانطور که ملاحظه می نمایید اولین آرگومان Delegate تعریف شده از نوع object می باشد. در مثالهایی واقعی تر و کاربردی تر عموما تنها یک مرجع از این شیء مورد استفاده قرار می گیرد. هر چند در مثال ما تنها یک new object() بعنوان آرگومان به رویداد گردان فرستاده شده است. از ریفرنس this نیز می توان در این حالت استفاده نمود. پارامتر دوم از System.EventArgs مشتق شده است. System.EventArgs کلاسی است پایه برای کپسوله کردن داده های مرتبط با رخدادها. ما از آن برای فرستادن اطلاعات مرتبط با رخداد به رویداد گردان استفاده می نماییم.
در ادامه کلاسی مشتق شده از EventArgs را ایجاد می نماییم:
کد:
public class DivBySevenEventArgs : EventArgs
{
public readonly int TheNumber;
public DivBySevenEventArgs(int num)
{
TheNumber = num;
}
}
متغیر read-only این کلاس برای نگهداری عدد تولید شده ی قابل قسمت بر هفت مورد استفاده قرار می گیرد. بجای اینکار از properties نیز می توان استفاده کرد.
سپس یک کلاس listener را برای گوش فرا دادن به رخدادهای اتفاق افتاده ایجاد می نماییم:
کد:
public class DivBySevenListener
{
public void ShowOnScreen(object o, DivBySevenEventArgs e)
{
Console.WriteLine(
"divisible by seven event raised!!! the guilty party is {0}",
e.TheNumber);
}
}
این کلاس حاوی متد ShowOnScreen می باشد که با نحوه ی تعریف Delegate برنامه در ابتدای ایجاد آن ، همخوانی دارد.
به نحوه ی استفاده از DivBySevenEventArgs برای نمایش عدد قابل قسمت بر هفت دقت نمایید.
در ادامه به آنالیز کلاس حاوی متد Main می پردازیم:
در این کلاس ، رخداد ذکر شده به صورت زیر تعریف می گردد :
کد:
public static event DivBySevenHandler EventSeven;
متد زیر رخداد را دریافت نموده و سپس تمام کلاینت ها را آگاه می سازد :
کد:
public static void OnEventSeven(DivBySevenEventArgs e)
{
if(EventSeven!=null)
EventSeven(new object(),e);
}
در تابع GenNumbers کدهای درون حلقه، 99 بار اجرا خواهند شد و در هر بار، عملیات بررسی قابل تقسیم بر هفت بودن ، انجام می شود.
کد:
public static void GenNumbers()
{
for(int i=0;i<99;i++)
{
if(i%7==0)
{
DivBySevenEventArgs e1 = new DivBySevenEventArgs(i);
OnEventSeven(e1);
}
}
}
در متد Main ابتدا شیء DivBySevenListener ایجاد می شود. سپس از عملگر += برای اضافه کردن delegate به رخداد استفاده گردیده است. بدیهی است که برای حذف می توان از عملگر -= استفاده کرد.
کد:
public static void Main()
{
DivBySevenListener dbsl = new DivBySevenListener();
EventSeven += new DivBySevenHandler(dbsl.ShowOnScreen);
GenNumbers();
}
پس از انجام این عملیات ، تابع GenNumbers فراخوانی می شود. این تابع اعدادی از 0 تا 98 را تولید می نماید. هرگاه که یکی از اعداد تولیدی بر هفت قابل قسمت باشد خبرش سریعا به اطلاع عموم خواهد رسید!
نکته :
رخدادها تنها در کلاسی که آنها را تعریف نموده اید قابل استفاده می باشند. این مورد می تواند مشکلاتی را در بحث ارث بری ایجاد نماید. برای حل این مساله می توان متد رخداد را protected معرفی نمود تا کلاس ارث برده از کلاس اصلی بتواند آنرا فراخوانی نماید و همچنین بهتر است آنرا virtual نیز معرفی کرد تا بتوان آنرا override (تحریف) نمود.
به صورت خلاصه برای بکار گیری رخدادها باید مراحل زیر طی شود:
1- تعریف یک Delegate
2- تعریف کلاسی مشتق شده از System.EventArgs برای کپسوله کردن اطلاعات مربوط به رخداد.
3- ایجاد کلاسی که رخداد را برانگیزد (rasing an event) .این کلاس شامل موارد زیر خواهد بود: الف) رخدادی که دارای کلاینت های رجیستر شده است. ب) متدی برای آگاه ساختن کلاینت ها از وقوع یک رخداد )
4- ایجاد کلاس کلاینت. این کلاس دارای متدی است که با امضای delegate همخوانی دارد (نوع و تعداد آرگومانهای ورودی و خروجی آنها یکی است ). این متد رخداد را دریافت خواهد کرد.
5- رجیستر کردن این متد بعنوان یکی از گوش دهندگان مجاز به استفاده از رخداد.
یک مثال کامل دیگر برای علاقمندان :
کد:
//Start of program
using System;
using System.ComponentModel;
// First step -- declare the delegate
public delegate void EvenEventHandler(object sender, EvenEventArgs e);
//Second step -- create a class derived from EventArgs
public class EvenEventArgs : EventArgs
{
// Declare private variables to reflect the information
// about the event
private readonly bool evensteven;
private readonly int evenone, eventwo;
//Constructor
public EvenEventArgs(bool evensteven, int evenone, int eventwo)
{
this.evensteven = evensteven;
this.evenone = evenone;
this.eventwo = eventwo;
}
//The properties to enable the client to access the event info
public bool Evensteven
{
get
{
return evensteven;
}
}
public int Evenone
{
get
{
return evenone;
}
}
public int Eventwo
{
get
{
return eventwo;
}
}
}
//Third step -- create the class that will raise the event
public class EvenDetector
{
//Declare some variables to make life simpler
private bool goteven, done;
//and our own random number generator
private Random r1;
//as well as the number we shall check for 'evenness'
private int randomnum;
//Also the two even numbers
private int evenone, eventwo;
//Constructor
public EvenDetector()
{
r1 = new Random();
}
public void numbercruncher()
{
//Loop until two successive even numbers have been generated
while(!done)
{
randomnum = (int)(100*r1.NextDouble());
if(randomnum%2==0)
{
if(goteven)
{
done =true;
eventwo = randomnum;
}
else
{
goteven = true;
evenone = randomnum;
}
}
else
{
goteven = false;
}
}
//Success -- create an object for the client(s)
EvenEventArgs e = new EvenEventArgs(done, evenone, eventwo);
//and call the method to send it to the client(s)
OnEven(e);
}
//Step 3a -- the event is declared
public event EvenEventHandler Even;
//Step 3b -- the method that notifies client(s)
protected virtual void OnEven(EvenEventArgs e)
{
// If the list of clients is NOT empty
if(Even != null)
{
//despatch the event to each client
Even(this, e);
}
}
}
//Fourth step -- and now the client class
public class EvenListener
{
//This is the method with the same signature as
//the delegate. It will receive the event
public void EvenAnnouncer(object sender, EvenEventArgs e)
{
//This will always be true and hence is redundant.
//Just illustrates the use of EventArgs
if(e.Evensteven)
{
Console.WriteLine("THE EVEN TWINS ARE HERE! -- {0} and {1}", e.Evenone, e.Eventwo);
}
}
}
//Fifth step -- hook 'em up
public class EvenTester
{
public static void Main()
{
//Instantiate EvenDetector
EvenDetector ed = new EvenDetector();
//Instantiate EvenListener
EvenListener el = new EvenListener();
//Register the listener method
ed.Even += new EvenEventHandler(el.EvenAnnouncer);
ed.numbercruncher();
}
}
//End of program
مراجع :
Events and event handling in C# By Nish (
CodeProject. Free source code and programming help )
Handling Events In C# By Biswajit Sarkar (
C# Help: C-Sharp Articles, Forum, Source Code)