Friday, September 12, 2008

Exceptions and Multiple Event Handlers

If you have multiple handlers for an event, when the event is raised, each of the handlers gets called in turn by the .NET Framework. So far, so good. What happens if one of the event handlers raises an exception? Then things don't go so well.

If any event listener raises an exception, the whole event-handling chain stops. If you pause to consider what's going on, this behavior makes sense. If an exception occurs in any listener, the exception bubbles back to the event-raising code, and the .NET Framework calls no more of the event listeners, and event handling grinds to a halt.

There is an alternative. Rather than simply raising the event, it is possible for you to call each individual listener explicitly. You can take advantage of the members of the Delegate class to solve the problem of unhandled exceptions in multiple listeners.



class Program {
static void Main(string[] args) {

BankAccount b = new BankAccount();
b.LargeAmountEvent += new LargeAmountEventHandler(b_LargeAmountEvent1);
b.LargeAmountEvent += new LargeAmountEventHandler(b_LargeAmountEvent2);
b.Withdraw(100);
}

// Throws an exception
static private void b_LargeAmountEvent1(Object sender, BankEventArgs e) {

if (true) {
throw new Exception("exception");
}
Console.WriteLine("Withdrawn" + e.Amount);
}


// Handler which does not throw an exception
static private void b_LargeAmountEvent2(Object sender, BankEventArgs e) {

Console.WriteLine("Withdrawn" + e.Amount);

}
}/*End Program*/


public class BankEventArgs : EventArgs {
private int amount;

public BankEventArgs(int amount) {
this.amount = amount;
}

public int Amount {
get { return amount; }
}
}



public delegate void LargeAmountEventHandler(Object sender, BankEventArgs e);



public class BankAccount {

public event LargeAmountEventHandler LargeAmountEvent;

public void Withdraw(int amount){
OnLargeWithdraw(new BankEventArgs(amount));
}

protected void OnLargeWithdraw(BankEventArgs e){

if (LargeAmountEvent != null) {
// Instead of raising event, call GetInvocationList
Delegate[] d = LargeAmountEvent.GetInvocationList();
foreach (LargeAmountEventHandler evnt in d) {
try {
evnt.Invoke(this, e);
} catch (Exception ex) {
Console.WriteLine(ex.Message);
}
}
//LargeAmountEvent(this, e);
}
}
}

No comments: