Click here to Skip to main content
15,891,943 members
Articles / Programming Languages / C#

An Event Pool

Rate me:
Please Sign up or sign in to vote.
4.68/5 (48 votes)
21 Feb 2004CPOL4 min read 153.8K   3.1K   105  
An event pool helps manage large amounts of events that otherwise clutter up your code and make maintenance difficult.
/*
Author: Marc Clifton

Copyright (c) 2004, Knowledge Automation
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

   Redistributions of source code must retain the above copyright
   and author notice, this list of conditions and the following disclaimer.
   
   Redistributions in binary form must reproduce the above copyright
   and author notice, this list of conditions and the following disclaimer in
   the documentation and/or other materials provided with the distribution.
   
   Neither the name of the Knowledge Automation nor the names of its contributors
   may be used to endorse or promote products derived from this software
   without specific prior written permission

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections;
using System.Diagnostics;

/// Knowledge Automation Event Pool
namespace KAEventPool
{
	/// <summary>
	/// A defensive event firing mechanism.
	/// Juval Lowy's and Eric Gunnerson's C# Best Practices
	/// download from: http://www.only4gurus.com/v2/preview.asp?ID=3183
	/// </summary>
	public class EventsHelper
	{
		/// <summary>
		/// Invoke all handlers attached to the multicast delegate, ensuring
		/// that the delegate exists, and iterating through the delegate collection
		/// so that event delegates with unassigned delegate handlers don't cause
		/// exceptions.
		/// </summary>
		/// <param name="del"></param>
		/// <param name="args"></param>
		public static void Fire(Delegate del, params object[] args)
		{
			if (del != null)
			{
				Delegate[] delegates = del.GetInvocationList();
				foreach(Delegate sink in delegates)
				{
					try
					{
						sink.DynamicInvoke(args);
					}
					catch(Exception e)
					{
						Trace.WriteLine(e.Message);
						throw(e);
					} 
				}
			}
		}
	} 
	
	/// <summary>
	/// EventPool simplifies the management of many different delegates, their associated events,
	/// and event handlers.  Events are abstracted by associating the event handler with a tag.
	/// Events are invoked by referring to the tag, and multicast delegates are used so that multiple
	/// handlers can be associated with the same event.
	/// </summary>
	public class EventPool
	{
		/// <summary>
		/// The multicast delegate definition.  Custom arguments should be derived from EventArgs.
		/// </summary>
		protected delegate void MulticastDelegate(object sender, EventArgs e);

		/// <summary>
		/// A one-to-one mapping of event tags to delegates managed by the event pool.
		/// </summary>
		protected Hashtable eventTagToDelegateMap;

		/// <summary>
		/// Constructor.
		/// </summary>
		public EventPool()
		{
			eventTagToDelegateMap=new Hashtable();
		}

		/// <summary>
		/// Publish the events that this pool will manage.
		/// </summary>
		/// <param name="tags">A list of tags that can be associated with event handlers.</param>
		virtual public void Publish(params object[] tags)
		{
			foreach(object tag in tags)
			{
				eventTagToDelegateMap.Add(tag, null);
			}
		}

		/// <summary>
		/// Subscribe to an event with validation that the event has been published.
		/// </summary>
		/// <param name="tag">The event tag.</param>
		/// <param name="eventHandler">The event handler.</param>
		virtual public void Subscribe(object tag, EventHandler eventHandler)
		{
			Subscribe(tag, eventHandler, false);
		}

		/// <summary>
		/// Subscribe to an event of the specified tag.
		/// </summary>
		/// <param name="tag">The tag that to be used when invoking the associated event handlers.</param>
		/// <param name="eventHandler">An EventHandler that receives the event, of the form "void (object sender, EventArgs args)"</param>
		/// <param name="loose">if false (the default), verifies that the event has been published.  If true, skips verification.</param>
		virtual public void Subscribe(object tag, EventHandler eventHandler, bool loose)
		{
			// do we verify that the handler exists?
			if (!loose)
			{
				if (!eventTagToDelegateMap.Contains(tag))
				{
					Trace.WriteLine("Event Pool does not contain an entry for: "+tag.ToString());
				}
			}

			// create the delegate for this event handler
			MulticastDelegate newDelegate=new MulticastDelegate(eventHandler);

			// if this is a new tag...
			if (!eventTagToDelegateMap.Contains(tag))
			{
				// ...add a new tag to the map.
				eventTagToDelegateMap.Add(tag, newDelegate);
			}
			else
			{
				// ...otherwise, combine the new delegate with the delegates for existing mapping.
				MulticastDelegate dlgt=(MulticastDelegate)eventTagToDelegateMap[tag];

				// if the tag has associated delegates already...
				if (dlgt != null)
				{
					// ... combine the new delegate with the existing ones.
					dlgt=(MulticastDelegate)Delegate.Combine(dlgt, newDelegate);
					// delegates are data structures, which are therefore passed by value.
					eventTagToDelegateMap[tag]=dlgt;
				}
				else
				{
					// ... otherwise just assign the new delegate.
					eventTagToDelegateMap[tag]=newDelegate;
				}
			}
		}

		/// <summary>
		/// Fire the events associated with the tag.
		/// </summary>
		/// <param name="tag">The event tag.</param>
		virtual public void FireEvent(object tag)
		{
			FireEvent(tag, null, null);
		}

		/// <summary>
		/// Fire the events associated with the tag, passing the supplied arguments.
		/// </summary>
		/// <param name="tag">The event tag.</param>
		/// <param name="args">The event arguments.</param>
		virtual public void FireEvent(object tag, EventArgs args)
		{
			FireEvent(tag, null, args);
		}

		/// <summary>
		/// Fire the events associated with the tag, passing the supplied sender.
		/// </summary>
		/// <param name="tag">The event tag.</param>
		/// <param name="sender">The originator of the event.</param>
		virtual public void FireEvent(object tag, object sender)
		{
			FireEvent(tag, sender, null);
		}

		/// <summary>
		/// Fire the events associated with the tag, passing the supplied sender and event arguments.
		/// </summary>
		/// <param name="tag">The event tag.</param>
		/// <param name="sender">The originator of the event.</param>
		/// <param name="args">The event arguments.</param>
		virtual public void FireEvent(object tag, object sender, EventArgs args)
		{
			Trace.WriteLine("EventPool Fire: "+tag.ToString());
			try
			{
				MulticastDelegate dlgt=(MulticastDelegate)eventTagToDelegateMap[tag];
				EventsHelper.Fire(dlgt, sender, args);
			}
			catch(Exception e)
			{
				Trace.WriteLine("Can't fire event tag: "+tag+"   "+e.Message);
			}
		}

		/// <summary>
		/// Fire all events in the pool.
		/// </summary>
		virtual public void FireAll()
		{
			FireAll(null, null);
		}

		/// <summary>
		/// Fire all events in the pool, passing the supplied sender.
		/// </summary>
		/// <param name="sender">The originator of the event.</param>
		virtual public void FireAll(object sender)
		{
			FireAll(sender, null);
		}

		/// <summary>
		/// Fire all events in the pool, passing the supplied arguments.
		/// </summary>
		/// <param name="args">The event arguments.</param>
		virtual public void FireAll(EventArgs args)
		{
			FireAll(null, args);
		}

		/// <summary>
		/// Fire all events in the pool, passing the supplied sender and event arguments.
		/// </summary>
		/// <param name="sender">The originator of the event.</param>
		/// <param name="args">The event arguments.</param>
		virtual public void FireAll(object sender, EventArgs args)
		{
			foreach (DictionaryEntry entry in eventTagToDelegateMap)
			{
				Trace.WriteLine("EventPool FireAll: "+entry.Key.ToString());
				MulticastDelegate dlgt=entry.Value as MulticastDelegate;
				EventsHelper.Fire(dlgt, sender, args);
			}
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Interacx
United States United States
Blog: https://marcclifton.wordpress.com/
Home Page: http://www.marcclifton.com
Research: http://www.higherorderprogramming.com/
GitHub: https://github.com/cliftonm

All my life I have been passionate about architecture / software design, as this is the cornerstone to a maintainable and extensible application. As such, I have enjoyed exploring some crazy ideas and discovering that they are not so crazy after all. I also love writing about my ideas and seeing the community response. As a consultant, I've enjoyed working in a wide range of industries such as aerospace, boatyard management, remote sensing, emergency services / data management, and casino operations. I've done a variety of pro-bono work non-profit organizations related to nature conservancy, drug recovery and women's health.

Comments and Discussions