For a .NET 2.0 version, see Pete O'Hanlon's article.
Preface
The EventPool is part of the Model-View-Controller (MVC) technology roadmap series, which includes:
- EventPool (this article)
- IMemento (tbd)
- UndoBuffer (tbd)
- StateManager (tbd)
- XmlGuiGenerator
- Form2Xml (tbd)
Introduction
Event pools are useful for managing large sets of events that you may typically encounter when working with an MVC model. A rich model often often contains many properties and undergoes many different state changes. Declaring a unique event (and sometimes a delegate with a subclassed EventArgs parameter) is time consuming and adds to the maintenance of the model.
The drawback to this approach is that subclassed EventArgs, while supported, must be cast to the correct subclass in the event handler. This moves the detection of incorrectly constructed event handlers from compile time to run time.
The Problem
Take a simple model, such as a four register stack used by a simple RPN calculator:
protected string[] registerStack={"0.00", "0.00", "0.00", "0.00"};
What we'd like to do is to provide an event that is fired whenever one of the register values changes so that any associated views can update the user interface, ex:

Let's say that the model represents the events with four properties:
Typically, we would construct a delegate for the event signature:
public delegate void RegisterChangeDelegate(object sender, EventArgs args);
and our four events:
public event RegisterChangeDelegate RegisterXChangeEvent;
public event RegisterChangeDelegate RegisterYChangeEvent;
public event RegisterChangeDelegate RegisterR3ChangeEvent;
public event RegisterChangeDelegate RegisterR4ChangeEvent;
and invoke our events in the property setters. For example, the X register setter:
public string X
{
get {return registerStack[0];}
set
{
registerStack[0]=value;
if (RegisterXChangeEvent != null)
{
RegisterXChangeEvent(this, EventArgs.Empty);
}
}
}
The view informs the model of it's interest in change event using:
model.RegisterXChangeEvent+=new EventHandler(RegisterXChangeEventHandler);
This will allow our views of the model to wire up event handlers that are invoked when the model changes, and all is happy with world. There are three problems:
- We've created event definitions that are a maintenance liability (albeit a small one in this example).
- Unless the programmer is very good, he/she will forget to instrument the event, as I have done here.
- We have to remember to always check if the event handler has actually been assigned.
The last problem can be worked around with some defensive event publishing, courtesy of Juval Lowy's and Eric Gunnerson's C# Best Practices Power Point presentation, downloadable here:
public class EventsHelper
{
public static void Fire(Delegate del,params object[] args)
{
if(del == null)
{
return;
}
Delegate[] delegates = del.GetInvocationList();
foreach(Delegate sink in delegates)
{
try
{
sink.DynamicInvoke(args);
}
catch{}
}
}
}
This eliminates the "if" block and ensures that, even if the event isn't assigned, the setter will still work. For example, this is how the new "X" setter would appear:
public string X
{
get {return registerStack[0];}
set
{
registerStack[0]=value;
EventsHelper.Fire(RegisterXChangeEvent);
}
}
Much cleaner, but it still doesn't address the other two problems--lots of events and lack of instrumentation.
The Solution
The solution is an event pool. The event pool simplifies the management of many different delegates and 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 (rather than the event delegate). The event pool also lends itself well to instrumentation so that the programmer doesn't have to remember to do this.
Given the above example, when we use an event pool, the following code is eliminated:
public delegate void RegisterChangeDelegate(object sender, EventArgs args);
public event RegisterChangeDelegate RegisterXChangeEvent;
public event RegisterChangeDelegate RegisterYChangeEvent;
public event RegisterChangeDelegate RegisterR3ChangeEvent;
public event RegisterChangeDelegate RegisterR4ChangeEvent;
The model fires any assigned events via the tag mechanism. For example:
public string X
{
get {return registerStack[0];}
set
{
registerStack[0]=value;
eventPool.FireEvent("X");
}
}
The view expresses its interest in the model's event in a slightly different manner:
model.EventPool.Subscribe("X",new EventHandler(RegisterXChangeEventHandler));
Loose Coupling
The danger, of course, is that the events to which you can subscribe are now very loosely associated with the events that published. How do you know if you are subscribing to an event that is still being published?
The answer is to tell the event pool what events you are publishing, via the Publish method:
virtual public void Publish(params object[] tags)
{
foreach(object tag in tags)
{
eventTagToDelegateMap.Add(tag, null);
}
}
The model, for example, publishes the events that it fires:
eventPool.Publish("X", "Y", "R3", "R4", "EntryStateChanged");
Now, when the view subscribes to the model's events, the event pool will verify that the event has been published and emit a warning if not (or you can change the code and have it throw an exception). The Subscribe method is overloaded so that a subscriber can optionally disable this checking, in the event (hahaha) that the publisher hasn't registered the events yet or is doing some dynamic event publication.
The Demonstration

The download includes an RPN calculator as a demonstration for the EventPool class. This demo a very clean MVC model and has a lot of events flying around, which are managed by the EventPool.
Documentation
Documentation for the EventPool can be found on my website. This documentation will be extended to include the other technologies in the MVC model as the additional articles in this series are published.
The only interesting thing about the EventPool is the Subscribe method, which builds a multicast delegate:
virtual public void Subscribe(
object tag,
EventHandler eventHandler,
bool loose)
{
if (!loose)
{
if (!eventTagToDelegateMap.Contains(tag))
{
Trace.WriteLine("Event Pool does not contain an entry for: "+
tag.ToString());
}
}
MulticastDelegate newDelegate=new MulticastDelegate(eventHandler);
if (!eventTagToDelegateMap.Contains(tag))
{
eventTagToDelegateMap.Add(tag, newDelegate);
}
else
{
MulticastDelegate dlgt=(MulticastDelegate)eventTagToDelegateMap[tag];
if (dlgt != null)
{
dlgt=(MulticastDelegate)Delegate.Combine(dlgt, newDelegate);
eventTagToDelegateMap[tag]=dlgt;
}
else
{
eventTagToDelegateMap[tag]=newDelegate;
}
}
}
Conclusion
I personally find this approach more maintainable and easier to use when working with MVC models that publish many events.
| You must Sign In to use this message board. |
|
|
 |
|
 |
dear mark non of your codes in codeproject could be run directly i dont know why but all of them need something a DLL or a missing main project and you use too many complex technique for a simple job
qwertyuiop
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Is there something against using enum as tags, instead of strings? With enum there's no need to remember all literal event names because it makes better use of intellisence. And also with enum there's no need to have the eventpool literally publish all events as string. Or do i overlook something here?
cheers! phil
Philippe Dykmans Software developpement University of Antwerp
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
philippe dykmans wrote: Is there something against using enum as tags, instead of strings?
Nope. Just didn't think of it.
Marc
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
It turned out that my idea of using enum instead of string was not that great at all.
Because the enum introduce a contract between all parties that use the eventpool. That is, the enum have to be visible to ALL of them. And so, there goes the 'loose coupling'.
Strings is better.
Phil
Philippe Dykmans Software developpement University of Antwerp
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
 | Thanks  Pete O'Hanlon | 2:00 30 Mar '07 |
|
 |
Marc
I really should have got round to thanking you sooner. The EventPool is a wonderful idea, and it fits into the applications that we are developing very neatly. If I could award you a 10 for this I would, but because I can't...
Deja View - the feeling that you've seen this post before.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Pete O`Hanlon wrote: If I could award you a 10 for this I would, but because I can't...
Thanks for the kind words, and I'm thrilled that it's helpful!
Marc
Thyme In The CountryInteracxPeople are just notoriously impossible. --DavidCrow There's NO excuse for not commenting your code. -- John Simmons / outlaw programmer People who say that they will refactor their code later to make it "good" don't understand refactoring, nor the art and craft of programming. -- Josh Smith
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
C# 2005 has something close to this. Its called the EventHandlerList which allows you to manage a large number of events. It would be a nice package to incorporate EventHandlerList with the way that Marc has here. The architecture in event pool is nicely done.
Ted
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Great job! This code fits my need. How about adding a Unsubscribe for removing subscriptions?
Thanks Marc, Ted
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This does the trick
virtual public void UnSubscribe(EventHandler eventHandler, string tag) { if (eventTagToDelegateMap.Contains(tag)) { EventPoolMulticastDelegate dlgt = (EventPoolMulticastDelegate)eventTagToDelegateMap[tag]; Delegate[] delegates = dlgt.GetInvocationList(); foreach (Delegate del in delegates) { if (del.Target.Equals(eventHandler)) { dlgt = (EventPoolMulticastDelegate)Delegate.Remove(dlgt, del); eventTagToDelegateMap[tag] = dlgt; break; } } } }
Funny thing I noticed was that the del Target is NOT the object instance of the passed eventHandler, rather it is the EventHandler itself - confused me for a while checking MSDN.
Gavin
Code Acuity
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I was getting an ExecutionEngineException during the sink.DynamicInvoke(args); call after upgrading to ASP.NET 2.0. The exception message stated "{The runtime has refused to evaluate the expression at this time.}".
It appears this was being caused by a naming collision with the declared delegate MulticastDelegate (conflicting with System.MulticastDelegate). After renaming the delegate to something else the problem was fixed.
This is some great code for working with MVC!
Michael Hamrah
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I also experienced a System.ExecutionEngineException when making a simple .NET 2.0 Windows console application to test the event pool. It was repeatable, and would come or go depending on the code (Trace.WriteLine()) I put in or left out of my main method.
Exception System.ExecutionEngineException was thrown in debuggee: <null reference>
The ultimate solution was to rename EventPool.MulticastDeligate to something else. It appears that there was a name collision as Michael suggested.
Thank you for the great example and implementation of an event pool, Mark!
Tim
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Given this, from your EventsHelper:
foreach(Delegate sink in delegates) { try { sink.DynamicInvoke(args); } catch{} }
What happens when the event handler you're firing contain coding errors that throw exceptions? Doesn't this pattern serve to hide those errors?
I've generally followed the principle that if something is wrong in the code, we must rely on exceptions to find them, but how can we do that, if we supress exceptions simply as a defensive measure when calling what may not be regarded as your code (in this case, the event handler) ?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I like this solution but I have a concern: the performance. As you say this solution is useful in project where you have a lot of different events but that means that you probably have a lot of generated events firing... and in this case the performance can be an issue.
I tried on my PentiumIV-3GHz to measure the time to fire an event and come back. The handler called does nothing. With the classic event handler I measured times i the order of 24 ns, with your event pool 3.5 us! That means 150 times slower!!
Am I doing something wrong?
Besides this, good job Marc! I'm waiting for more articles.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Marc,
the event pool crashes when combining to multicastdelegates. Did you notice this? Combining delegates only seems to work.
Regards Robert
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I tried to work with the Eventpool But I have a problem with the debugger: ... Delegate[] delegates = del.GetInvocationList(); foreach(Delegate sink in delegates) { try { sink.DynamicInvoke(args); } catch(Exception e) { Trace.WriteLine(e.Message); throw(e); } } ...
The following code is in another project: ... private void method1(object sender, EventArgs e) { System.Diagnostics.Debugger.Break(); Console.WriteLine("I was in method1"); } ...
Method1 will be called by the delegate. And it works!!! You can see the text at console. But the debugger don't stopp at the line "Console.WriteLine("I was in method1");", where a breakpoint was set. Then I tried the line "System.Diagnostics.Debugger.Break();". Now the program stops at "sink.DynamicInvoke(args);". The line is green. It seems as if the debugger can't find the sourcecode for method1.
The funny thing: If I call the eventshelper alone, the debugger will stop. If I call the eventpool, the debugger will not stop when the eventshelper calls the method.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I find the idea of an event pool very intriguing, but there's one thing that bothers me: you lose the ability to automatically document the events that the class is offering to the outside world.
Documentability is a big issue in the company where I work, and we routinely use ndoc to generate reference documents, which also (automatically) include the events.
With the event pool approach this advantage of automatically including the events in the documentation disappears.
One of the arguments you make against "traditional event programming", the maintenance liability, can imo be addressed by using a code generation tool like Workstate's Codify to generate the events, delegate declarations, event arguments classes and so on.
These thoughts leave me with the idea "an event pool is a nice idea but has little practical value". Even so, I can't help wondering if there is something about the event pool that I missed?
Dirk
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Dirk Rombauts wrote: Documentability is a big issue in the company where I work
The examples in the article are all code (imperative programming). In reality, when I use the EventPool, I use declarative programming parsed by MyXaml. This creates a human-readable form for publishing events and event subscribers. Since it's XML, it's also very easily converted into code documentation.
Dirk Rombauts wrote: a code generation tool
I'm not particularly fond of code generation tools. You end up customizing some auto-generated code and then it gets overwritten the next time you generate. They usually don't follow the paradigm that I use for programming and they create code which is more liability, not less. But that's just my personal preference.
I would say though--
an event pool is a nice idea but has little practical value
That this is a bit like throwing out the baby with the bathwater. Are you throwing out the concept because it can't be documented?
Marc
MyXaml Advanced Unit Testing
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Marc Clifton wrote: Are you throwing out the concept because it can't be documented?
That is more or less what I am saying - on the one hand. On the other hand I do think that an event pool is a neat idea, and I am sort of looking for a way to see the practical usefulness of the idea.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I finally had a chance to sit down and get familiar with the dirty details of MVC, and I find myself eating those words I wrote a year ago. There still is the issue of documentability, but I am now of the opinion that the benefits of the event pool far outweight the drawback of that issue.
I could imagine a couple of workarounds: - Publishing goes according to a rather fixed schema, so a regular expression should fish out the published events. - I try to make use of code generators for simple, repetitive classes. I could generate derived classes of EventPool that additionally expose strongly typed events. NDoc will do the rest as far as documentation is concerned.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Great implementation. I don't even have tried the code as I am interested in the theorical concept. We are developing a corporate scheduler component and an event pool will be perfect for the multi-tier objects inside the control.
Good Job. Greetings
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
there is a problem in the source code when reading from the xml files as follows: - the system uses regional settings "list seperator" to parse the location points etc (e.g. 10,10 won't work when the system list char is ';')
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Probably I didn't understand something on your article, but this really reminds me about windows messages and MFC's message maps. Only that WM_ messages are ints, not strings like in your model. It's strange adding another layer over other layers to get functionality available on the previous layers: WM_ messages -> Message maps -> .NET Events -> Event Pool (which provides the message maps functionality). Probably I'm really tired right now and didn't understand your article... 
Perl combines all the worst aspects of C and Lisp: a billion different sublanguages in one monolithic executable. It combines the power of C with the readability of PostScript. -- Jamie Zawinski
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Events and delegates don't directly correlate to messages and the MFC message map. Rather, they correspond to functors--function pointers. However, they're a bit more flexible because they support multicasting--you can assign more than one function to the functor, and C# will invoke them in sequence.
.NET events are quite flexible, but it's sure a PITA to have to write the delegate which prototypes the handler, the event declaration for the event that gets fired, and the event handler itself. Using the EventPool, the first two steps are avoided.
I personally think it makes the management of events a lot easier, especially when there are hundreds of events flying around, which happens frequently in an MVC architecture for a complicated application. The instrumentation is nice benefit--you can see the sequence in which the events are being fired. If something breaks, the outer call's catch will show you where, but the application keeps on trucking.
It's wierd. Some of my code throws exceptions but everything still works OK. (Of course, I fix the exceptions!) Just goes to show the complexity of the application I'm working on.
Marc
Microsoft MVP, Visual C#
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|