|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThere are a number of articles available which deal with delegates. The objective of this article is to present events and delegates in a very straightforward fashion, which will enable and encourage developers to include events in classes that they design and develop, as well as properties (attributes), and methods (operations). Understanding eventsTo help understand events, consider the If our web page wants to subscribe to, and respond to the The generated code looks like this: private void Page_Load(object sender, System.EventArgs e)
{
// add an event handler to deal with
// the SelectedIndexChangedEvent
cboDropDown.SelectedIndexChanged +=
new EventHandler(cboDropDown_SelectedIndexChanged);
}
private void cboDropDown_SelectedIndexChanged(
object sender, EventArgs e)
{
}
When the application runs, if the user selects a new option from the drop down list, the method How to build events into classesLet us consider an object that is being constructed using C# to implement the business rules behind creation of invoices: public class Invoice
{
private string strInvoiceID;
private System.DateTime dtmDateRaised;
public Invoice()
{
}
public string invoiceID
{
get
{
return this.strInvoiceID;
}
set
{
this.strInvoiceID = value;
}
}
public System.DateTime dateRaised
{
get
{
return this.dtmDateRaised;
}
set
{
this.dtmDateRaised = value;
}
}
}
We are going to suppose that the client application can contain multiple views of the invoice, and that should the invoice be changed in any way, then all of the views would like to be informed such that they can independently update their user interfaces. The set of steps involved in creating an event to enable this functionality for clients of objects constructed using this class is detailed below: 1. Create a sub-class of the EventArgs classWhen we constructed the event handler associated with the private void cboDropDown_SelectedIndexChanged(
object sender, EventArgs e)
{
}
When constructing our own class that can raise events, we can have whatever parameters we like, but we will opt to stick to the standards that the Microsoft .NET Framework objects adhere to. The first parameter - The second parameter - public class BusinessObjectEventArgs : EventArgs
{
private string strPropertyName;
private object objOldValue;
private object objNewValue;
public BusinessObjectEventArgs(string PropertyName,
object OldValue, object NewValue)
{
this.strPropertyName = PropertyName;
this.objOldValue = OldValue;
this.objNewValue = NewValue;
}
public string PropertyName
{
get
{
return this.strPropertyName;
}
}
public object OldValue
{
get
{
return this.objOldValue;
}
}
public object NewValue
{
get
{
return this.objNewValue;
}
}
}
Now when an event handler is being called because a property within the object of class 2. Create the delegateThe next step is to create the all-important delegate. Consider the following delegate: public delegate void BusinessObjectEventHandler(
object sender, BusinessObjectEventArgs e);
A delegate may be thought of as the blue-print, or interface for a call-back function. When an event in the Understanding delegates really does come down to that simple definition. A delegate is the interface definition of a call-back function. 3. Create the eventOnce an public event BusinessObjectEventHandler OnChanged;
The 4. Raise the eventWhen the client application uses the The last thing left to do in the public string invoiceID
{
get
{
return this.strInvoiceID;
}
set
{
if (OnChanged != null)
{
OnChanged(this,
new BusinessObjectEventArgs("invoiceID",
this.strInvoiceID, value));
}
this.strInvoiceID = value;
}
}
public System.DateTime dateRaised
{
get
{
return this.dtmDateRaised;
}
set
{
if (OnChanged != null)
{
OnChanged(this,
new BusinessObjectEventArgs("dateRaised",
this.dtmDateRaised, value));
}
this.dtmDateRaised = value;
}
}
Instantiating an Invoice object and subscribing to its eventsWith the changes to the using BusinessObjects;
using System.Diagnostics;
...
private void AnInvoiceClient()
{
BusinessObjects.Invoice i = new Invoice();
i.invoiceID = "1";
i.OnChanged +=
new BusinessObjectEventHandler(i_OnChanged1);
i.OnChanged +=
new BusinessObjectEventHandler(i_OnChanged2);
i.dateRaised = System.DateTime.Now;
}
private void i_OnChanged1(object sender,
BusinessObjectEventArgs e)
{
string strMsg = "object {0} property " +
"{1} changed from {2} to {3}";
BusinessObjects.Invoice i = (Invoice)sender;
Debug.WriteLine(String.Format(strMsg,
i.invoiceID, e.PropertyName,
e.OldValue, e.NewValue));
}
private void i_OnChanged2(object sender,
BusinessObjectEventArgs e)
{
string strMsg = "object {0} property " +
"{1} changed from {2} to {3}";
BusinessObjects.Invoice i = (Invoice)sender;
Debug.WriteLine(String.Format(strMsg,
i.invoiceID, e.PropertyName,
e.OldValue, e.NewValue));
}
When object 1 property dateRaised changed from
01/01/0001 00:00:00 to 21/10/2005 23:18:35
object 1 property dateRaised changed from
01/01/0001 00:00:00 to 21/10/2005 23:18:35
Note that the client subscribes to the event twice. When the The complete code for the finished Invoice.cs implementation of the using System;
namespace BusinessObjects
{
public class Invoice
{
private string strInvoiceID;
private System.DateTime dtmDateRaised;
public event BusinessObjectEventHandler OnChanged;
public Invoice()
{
}
public string invoiceID
{
get
{
return this.strInvoiceID;
}
set
{
if (OnChanged != null)
{
OnChanged(this,
new BusinessObjectEventArgs("invoiceID",
this.strInvoiceID, value));
}
this.strInvoiceID = value;
}
}
public System.DateTime dateRaised
{
get
{
return this.dtmDateRaised;
}
set
{
if (OnChanged != null)
{
OnChanged(this,
new BusinessObjectEventArgs("dateRaised",
this.dtmDateRaised, value));
}
this.dtmDateRaised = value;
}
}
}
public class BusinessObjectEventArgs : EventArgs
{
private string strPropertyName;
private object objOldValue;
private object objNewValue;
public BusinessObjectEventArgs(string PropertyName,
object OldValue, object NewValue)
{
this.strPropertyName = PropertyName;
this.objOldValue = OldValue;
this.objNewValue = NewValue;
}
public string PropertyName
{
get
{
return this.strPropertyName;
}
}
public object OldValue
{
get
{
return this.objOldValue;
}
}
public object NewValue
{
get
{
return this.objNewValue;
}
}
}
public delegate void BusinessObjectEventHandler(
object sender, BusinessObjectEventArgs e);
}
ConclusionThe original premise was that an application envisaged having multiple views of the same With the following four simple steps:
The A footnote on designing for eventsEvents should be an intrinsic part of any class design. An object oriented designer / developer will automatically ask "What properties should my class expose?" and "What methods should my class support?". They will seldom think, "What events should my class raise?" Adding support for events in C# classes is very straightforward, and enhances those classes by adding powerful functionality that cannot be added in any other way. Even if there is no immediate need for events within a class, designers and developers should think about it in future. Often, objects go into production, and end up being consumed by client applications unforeseen in the original design. Even if there is no immediate need for events, if they exist, they will almost certainly be utilized at some time or the other, as the class is instantiated, and used. Design useful events into classes today and avoid having to re-code those classes tomorrow. However, not only are delegates (and therefore events) often over-looked in C# development, but the UML 2.0 standard for class diagrams does not include anywhere to design events into a class (at least not that I have found). If there is no way for a class designer to indicate that they would like events to be built into a class, how are they ever going to get developed? My recommendation is to use the «event» stereotype when developing UML class diagrams to indicate the design for the event as follows:
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||