Creating Custom Delegates and Events in C#

Controls used on your forms have events that you can respond to with event handlers in your application. C# makes it easy to have the same behavior in other parts of your application by creating your own delegates and events, and raising the events based on your program logic. In this article we will examine delegates and events, how they relate, and how to use them to create custom events for your applications.

Delegates

A delegate is a class that can contain a reference to an event handler that matches the delegate signature. It provides the object oriented and type-safe functionality of a function pointer. The delegate is implemented by the .NET runtime environment; all you need to do is declare one with the appropriate signature. It is customary for delegates to have two arguments, an object type named sender and an EventArgs type named e. When you double click on a control in the forms designer of Visual Studio.NET, you are provided with a stub for an event handler that matches this signature. This signature is not required and you may want a different signature for your custom events. When a delegate is created and added to an event invocation list, the event handler is called when the event is raised. Multiple delegates can be added to a single event invocation list and will be called in the order they were added.

Events

Events are notifications, or messages, from one part of an application to another that something interesting has happened. When an event is raised, all the delegates in the invocation list are executed in the order they were added. The sender of the event does not know which part of the application will handle the event, or even if it will be handled. It just sends the notification and is finished with its responsibilities. It is often necessary to provide some information about the event when the notification is sent. This is normally included in the EventArgs argument to the event handler. You will probably want to develop a class derived from EventArgs to send information for your custom events.

Suppose you are creating an application for a bank to manage accounts and want to raise an event when the balance falls below some minimum balance. There is no button to click when this happens, and you would not want to depend on a person noticing the balance is low and performing some action to indicate the balance is low to the rest of the application. A low balance happens because of a withdrawal of some amount that reduces the account balance below the minimum required balance. You want the notification to be automatic, so appropriate action can be taken by some other part of the application without user intervention. This is where custom delegates and events are useful. The following code will demonstrate delegates and events for this example.

// First, create a class for our event arguments that is derived from EventArgs.
public class AccountBalanceEventArgs : EventArgs
{
// Include properties for the account number, current balance, required
// minimum balance, a message describing the event, and a transaction ID.
// You can include any information that would be useful for you application.
private string acctnum;
public string AccountNumber
{
get
{
return acctnum;
}
}

private decimal balance;
public decimal AccountBalance
{
get
{
return balance;
}
}

private decimal minbal;
public decimal MinimumBalance
{
get
{
return minbal;
}
}

private string msg;
public string Message
{
get
{
return msg;
}
}

private int transID;
public int TransactionID
{
get
{
return transID;
}
}

// AccountBalanceEventArgs constructor
public AccountBalanceEventArgs(string AcctNum, decimal CurrentBalance,
decimal RequiredBalance, string MessageText, int transaction)
{
acctnum = AcctNum;
balance = CurrentBalance;
minbal = RequiredBalance;
msg = MessageText;
transID = transaction;
}
}

// Now create a delegate for the event notification messages. It has a return
// type of void and takes an instance of our AccountBalanceEventArgs class as
// the only argument. It does not have to be declared inside a class.
public delegate void AccountBalanceEventHandler(AccountBalanceEventArgs);

// Create a class that will send the notification messages
public class Account
{
// Create an event for you class
// It has the form public event delegateName eventName
public event AccountBalanceEventHandler AccountBalanceLowEvent;

private string acctnum;
private decimal balance;
private decimal minBalance;

// This method could cause the balance to fall below the minimum required
// so we will use the event here.
// transID is some extra information about which transaction caused
// the event to be raised, so we send it along to be included with
// our event arguments.
public void Withdraw(decimal amount, int transID)
{
// if the transaction would reduce the balance below the minimum,
// raise the event
if ((balance – amount) < minBalance)
{
DispatchAccountBalanceLowEvent(transID);
}
else
{
// everything is ok, so reduce the balance and no event is raised
balance -= amount;
}
}

// This method adds an event handler (delegate) to the event invocation list.
// Any class that has a method that returns void and takes a single
// AccountBalanceEventArgs argument can subscribe to this event.
public void SubscribeAccountBalanceLowEvent(AccountBalanceEventHandler eventHandler)
{
AccountBalanceLowEvent += eventHandler;
}

// This method removes an event handler (delegate) from the event invocation
// list.
// Any class that has subscribed to the event can unsubscribe
public void UnsubscribeAccountBalanceLowEvent(AccountBalanceEventHandler eventHandler)
{
AccountBalanceLowEvent -= eventHandler;
}

// This method raises the event which causes all the event handlers in the event
// invocation list to be executed in the order they were added.
private void DispatchAccountBalanceLowEvent(int transaction)
{
// make sure the are some delegates to call
// all delegates will be called in the order they were added
// to the invocation list
if (AccountBalanceLowEvent != null)
{
AccountBalanceLowEvent(new AccountBalanceEventArgs(
acctnum, balance, minBalance,
"Withdrawal Failed: Account balance would be below minimum required",
transaction));
}
}

// the rest of the implementation is omitted
}

// Now create a class to handle the events. Name it whatever is meaningful for
// your application.
public class EventHandlerClass
{
// This method will be an event handler. It can be named anything you want,
// but it must have the same signature as the delegate
// AccountBalanceEventHandler declared above.
public void HandleAccountLowEvent(AccountBalanceEventArgs e)
{
// do something useful here
// e.AccountNumber, e.AccountBalance, e.MinimumBalance,
// e.Message, and e.TransactionID are all available to use
}

// This method will be another event handler. It can be named anything you
// want, but it must have the same signature as the deleg

ate /> // AccountBalanceEventHandler declared above.
public void HandleAccountLowEvent2(AccountBalanceEventArgs e)
{
// do something useful here
// e.AccountNumber, e.AccountBalance, e.MinimumBalance,
// e.Message, and e.TransactionID are all available to use
}

// the rest of the implementation is omitted.
}

We now have all the code to implement our custom event and have it handled by our event handlers. All we need is to tie everything together. Somewhere in your code, where it makes sense for your application, you would create instances of Account and EventHandlerClass, subscribe to the event notification, make a withdrawal and do something useful if the event is received.

// somewhere in your code…

EventHandlerClass handler = new EventHandlerClass();
Account acct = new Account();
acct.SubscribeAccountBalanceLowEvent(
new AccountBalanceEventHandler(handler.HandleAccountLowEvent));
acct.SubscribeAccountBalanceLowEvent(
new AccountBalanceEventHandler(handler.HandleAccountLowEvent2));
// if the next line causes the current balance to fall below the mimimum
// balance, the event will be raised and handler.HandleAccountLowEvent will be
// called followed by handler.HandleAccountLowEvent2
acct.Withdraw(1000.00M, 1);

acct.UnsubscribeAccountBalanceLowEvent(
new AccountBalanceEventHandler(handler.HandleAccountLowEvent2));
// now only handler.HandleAccountLowEvent will be called if the event is raised
acct.Withdraw(1000.00M, 2);

acct.UnsubscribeAccountBalanceLowEvent(
new AccountBalanceEventHandler(handler.HandleAccountLowEvent));
// now no event handlers will be called
acct.Withdraw(1000.00M, 3);

As you can see, it is not difficult to implement our own delegates and eventsthat allow us to send a notification message from one part of our applicationto another that something interesting has happened. C# provides all the necessary tools to enable this capability with a minimum of effort. It is well worth adding this capability to your software when you need to handle an interesting event.

Twitter Digg Delicious Stumbleupon Technorati Facebook Email

No comments yet... Be the first to leave a reply!