(c) 2002 Visual Studio Magazine
Fawcette Technical Publications

Issue: April 2002
Section: Tame .NET Events
Author: Juval Lowy

C#	Prevent Subscribers From Modifying Event Arguments
Listing 1	By default, one subscriber can affect all other subscribers that handle the event after it by changing the event arguments. You can prevent subscribers from modifying the event arguments by using either a read-only property, or applying the readonly access modifier to a public member. 

public class NumberEventArgs1 : EventArgs
{
	public readonly int Num;
	NumberEventArgs1(int num)
	{
	Num = num; 
	}
}
public class NumberEventArgs2 : EventArgs
{
	protected int m_Num;
	NumberEventArgs2(int num)
	{
		m_Num = num;
	}
	int Num
	{ 
		get
		{
			return m_Num;
		}
	}
}


C#	Encapsulate Event Delegate Member Variables
Listing 2	Event accessors encapsulate the actual event delegate member variable (note the "protected" modifier). Accessors allow the same client code as they do with raw public event delegate members.

public delegate void NumberChangedDelegate(int 
	num);

public class MySource
{
	protected event NumberChangedDelegate 
		m_NumberChangedEvent;
		public event NumberChangedDelegate 
			NumberChangedEvent
		{
			add
			{
				m_NumberChangedEvent += value;
			}
			remove
			{
				m_NumberChangedEvent -= value;
			}
		}
		public void FireEvent(int num)
		{
			m_ NumberChangedEvent(num);
		}
	} 

MySource source = new MySource();
MySink   sink   = new MySink();

//Setup connection:
source.NumberChangedEvent += new 
	NumberChangedDelegate(sink.OnNumberChanged);
//Fire Event
source.FireEvent(42);
//Teardown connection:
source.NumberChangedEvent -= new 
	NumberChangedDelegate(sink.OnNumberChanged);

C#	Manage Large Numbers of Events
Listing 3	Use the EventHandlerList class when dedicating member variables per event is impractical. EventHandlerList stores value/key pairs identifying the event and its handler. You can use any .NET object as a key.

using System.ComponentModel;

EventHandlerList eventList;
eventList = new EventHandlerList();

//To add to the list:
eventList.AddHandler(m_Button1,new 
	EventHandler(OnButtonClicked));

//To fire event:
EventHandler handler = (EventHandler)eventList[m_Button1];
handler(m_Button1,EventArgs.Empty);

//To remove from the list:
eventList.RemoveHandler(m_Button1,new 
	EventHandler(OnButtonClicked)); 

C#	Manage Sink Interfaces
Listing 4	The publisher can further encapsulate the actual event implementation by providing methods that manage connection to sink interfaces. This schema also saves round trips and promotes loose coupling between the publisher and its subscribers.

public class MySource
{
	protected event EventHandler m_event1;
	protected event EventHandler m_event2;
	protected event EventHandler m_event3;

	public void Advise(IMySink sink,EventType 
		eventType)
	{
		if((eventType & EventType.OnEvent1) == 
			EventType.OnEvent1)
		{
			m_event1 += new 
				EventHandler(sink.OnEvent1); 
		}
		if((eventType & EventType.OnEvent2) == 
			EventType.OnEvent2)
		{
			m_event2 += new 
				EventHandler(sink.OnEvent2);
		}
		//if EventType.OnEvent3...
	}
	public void Unadvise(IMySink sink,EventType 
		eventType)
	{
		if((eventType & EventType.OnEvent1) == 
			EventType.OnEvent1)
		{
			m_event1 -= new 
				EventHandler(sink.OnEvent1);
		}
		if((eventType & EventType.OnEvent2) == 
			EventType.OnEvent2)
		{
			m_event2 -= new 
				EventHandler(sink.OnEvent2);
		}
		//if EventType.OnEvent3...
	}
	public void FireEvent(EventType eventType)
	{
		if((eventType & EventType.OnEvent1) == 
			EventType.OnEvent1)
		{
			m_event1(this,EventArgs.Empty);
		}
		if((eventType & EventType.OnEvent2) == 
			EventType.OnEvent2)
		{
			m_event2(this,EventArgs.Empty);
		}
		//if EventType.OnEvent3...
	}
}

C#	Fire Events Asynchronously
Listing 5	You can't call BeginInvoke() on an event delegate, because the delegate will probably have more than one callback. Instead, use GetInvocationList() to access the callback list, and call BeginInvoke() on each sink delegate yourself.

public delegate void NumberChangedDelegate(int 
	num);

public class MySource
{
		public event NumberChangedDelegate 
			m_NumberChangedEvent;

		public void FireEventAsynch(int num)
		{
			Delegate[] delegates = 
				m_NumberChangedEvent.
				GetInvocationList();
			foreach(Delegate del in delegates)
			{
				NumberChangedDelegate sink = 
					(NumberChangedDelegate)del;
				sink.BeginInvoke(num,null,null);
			}
		}
		public void FireEvent(int num)
		{
			m_ NumberChangedEvent(num);
		}
		}
