IoC By Way Of Simple Event Raising: Part 1

by Jon Davis 14. August 2008 20:20

There's a crappy and tiny little sample project available for this post at: IoCEvents_Part1.zip from which one can experiment with commenting things out and exciting stuff like that.

So I've noticed that the latest sexy trend for the last couple years in software development has been to adopt these frameworks that facilitate IoC, or Inversion of Control. Among the "sexy" toys that people have adopted in .NET space are Castle Windsor, Spring.net, StructureMap, et al. 

I have little doubt that I'm going to get knocked a few brownie points for this post. But I have a really tragic confession to make: I don't get it. I see the purpose of these tools, but sometimes I wonder if architects have picked up the virus that causes them to add complexity for the sake of complexity. These tools are supposed to help us make our lives easier, but the few exposures I've had of them have had me scratching my head because some of them introduce so much complexity that they seem a little silly. Maybe my brain is just a little too average in size. I like to think in simple terms.

And I get overwhelmed by the complexity when I try to read up on Castle Windsor, Rhino Mocks (which is not IoC but complements it), and other ALT.NET frameworks that have made inroads to making .NET solutions "agile" and testable. I look at things like [this] and while perhaps a lot of architects and ALT.NET folks are thinking, "cool", I'm scratching my head and thinking, "eww!!" I'm not very savvy with Rhino Mocks nor Castle Windsor but looking at blog entries like that I'm frankly scared to get to know them.

It's not that I'm stupid or that I lack knowledge or discipline. It's that I have the firm belief that things that require complex attention and thought should be isolated from the practical business logic and "everyday code", including their test code and the containing class library projects themselves. Were I to take on a role of architecure, I would do everything I can to be as mainstream and lightweight as possible, so that any junior programmer can see and understand what I'm doing with a little effort, and any senior developer can read and understand what's going on without even thinking about it. Granted, I haven't been all that great at it, but it's something to strive for, and not something I see when I watch people build up dependencies upon mocking and IoC frameworks.

Introduction Of New Dependencies Is Not Clean IoC. (Or, "Just Say 'No!' To IWindsor!")

Here's where I'm most annoyed by what little I've seen of IoC container toolkits: they introduce a dependency upon the IoC container toolkit. My first exposure to an IoC container in the workplace (as opposed to looking at someone else's solution such as Rob Conery's videos) was an awfully ugly one. Legacy code that I already despised but had been more or less stablized over years of maintenance and hand-holding, now having partial implementations of integration with Castle Windsor, was now throwing bizzare exceptions I'd never seen before. The exceptions being thrown by Castle were, to my untrained eye, utterly incomprehensible. This was while we tried to scramble to update the codebase so that my latest changes could get rolled out. I was told to add huge blocks of configuration settings in the web.config file. Adding those config file changes, now all kinds of new errors were showing up. "Oops. I guess I'm not done yet", the architect muttered. Knowing it could be days before he could be "done", and I needed to roll out my changes by the end of the day, I decided to roll back to the earlier legacy codebase that had no dependency upon Castle.

Since then, they've cleaned it up and rolled out the revisions with Castle dependency, which is great (and I've since quit) but when it comes to inversion of control I still don't understand why everything must become more brittle and more heavily dependent upon third party frameworks, particularly when the project needing IoC exists as a standalone set of business objects. Ask yourself, "What direct correlation do my business objects have with this massive IoC subsystem? Do I want to introduce kludge to this, or should my business objects be as pure as I can make them, in the interest of the original premise of IoC, which is to make the objects or functionality as completely agnostic to, but cooperative with, external projects and libraries as possible?" Or at least, that's what I think the objectives should be with IoC.

Meanwhile, I saw a video on Rob Conery's blog where he was exploring the utilization of an IoC framework, and I still have myself asking the question, why? You already have a "know it all" mapping class, why not just map things together with basic event handlers?

C# Already Has An IoC Framework. It's Called The "Event".

Dependency injection and IoC are not rocket science, or at least they shouldn't be. Theoretically, if something needs something from someone else, it should ask its runtime environment, "Um, hey can someone please handle this? I don't know how." That's why events exist in .NET.

Events in .NET are often misunderstood. They're usually only used for UI actions -- someone clicks on a button and event gets raised. Certainly, that's how the need for events started; Windows is an events-driven operating system, after all. Nearly all GUI applications in Windows rely on event pumps.

In my opinion, Java's Spring Framework, which is perhaps the most popular dependency injection framework on the planet, became popular because Java didn't have the same eventing subsystem that .NET enjoys. It's there, but, originally at least (I haven't done much with Java since 1999), it wasn't very versatile. And likewise, in my opinion, the .NET world has begun to adopt Spring.NET and Castle Windsor because non-.NET languages have always needed some kind of framework in order to manage dependency injection, and so the Morts are getting "trained in" on "the way things are done, the comp-sci way". On this, I think the ALT.NET community might've gotten it wrong. 

If you think of .NET events as being classic IoC containers instead of GUI handlers, suddenly a whole new world begins to open up.

In the GUI realm, the scenario has changed little over the last decade. When a mouse moves and a button clicks, something happens in IoC space that should sound familiar:

  1. The mouse driver doesn't know if something is going to handle the circumstances, but it is going to raise the event anyway.
  2. Windows passes the event along to the GUI application. It doesn't know if the application is going to handle the circumstances, but it is going to raise the event anyway.
  3. The application passes the event along to the control that owns the physical space of the screen coordinance of the mouse. It doesn't know if the control is going to handle the circumstances, but if the control has subscribed to the event, it is going to raise the event anyway.
  4. The control is a composite control that has a button control at the mouse's coordinates. It gets "clicked", and another event gets raised, which is the click event of a button control. The raising of the event is done internally in .NET, and .NET doesn't know how the event is going to be handled, but if the application has subscribed to the button control's event handler, it raises the event anyway.
  5. The application processes the button control's click event and invokes some kind of business functionality, such as "PayCustomer()".

In this way, Windows and the application became an IoC mapping subsystem that mapped mouse coordinates and actions to particular business functionality.

If you apply the same principle to non-GUI actions, and use events in C# interfaces for everyday object member stubs, and follow an events-driven IoC pattern, you can easily find the mapping of disperate functionality to be pluggable and fully usable as first-class functionality baked right into C#.

Events And Event Handlers Are Truly Dependency-Neutral

Event handlers are just the dynamic invocation of delegates. There are no other significant qualifications for events or event handlers, other than the obvious:

  • The events themselves must have accessors (public, protected, etc.) that enable the handler to "see" the event in the first place.
  • The raised event's argument types and the return value type might require external library reference inclusions in the event handling project.
    • On the flip side the event handler can offer dependencies that the event raiser knows nothing about, which is where dependency injection comes into play.

Most people reading this already know all about how events and delegates in C# work, but I think it's very possible that C# programmers have had difficulty putting two and two together and discovering the versatility of event delegation. I suppose it's possible that some things got assumed that shouldn't have been.

Incorrect Assumption #1: Events Should Only Be Used For GUI Conditions

The fact is you can use events pretty much anywhere you want. Got some arcane business object that "does something" now and then? You can use an event there. Seriously. But you knew that.

Incorrect Assumption #2: Events Should Strictly Adhere To .NET's Pattern Of Event Handler Signatures

The common event handler signature that Microsoft uses in .NET 2.0 is seen in the EventHandler delegate:

delegate void EventHandler (object sender, EventArgs e);

This is actually not a desirable signature for business objects. It's fine in GUI applications, and, while a little awkward, it's heavily used in ASP.NET Web Forms. But it's definitely not very useful in specifically-crafted class libraries where handler behaviors are assumed and sometimes required.

You do not need to pass object sender, although you probably should send the sender object anyway. Typed.

The reason why object sender exists in the standard pattern is because the event handler, being a delegate, can be reused to handle events having identical signatures on behalf of multiple different objects. But if there is, by design, a one-to-one relationship between the handled object and the object's handler, the sender can be safely inferred.

That said, though, if you do pass the sender along, there's no significant need to send it as type object. Send it as the lowest common denominator of what it can be. I've wasted so much time typecasting the sender in my event handlers I'm actually annoyed that "object sender" is a pattern; in Win Forms, the lowest common denominator should be System.Windows.Forms.Control, not object; and in the Web Forms world, the lowest common denominator should be System.Web.UI.Control, not object.

You do not need to pass an EventArgs object.

EventArgs is a generic argument container object. It is, in itself, an empty object, but inheriting it and adding your own properties enables you to reuse the Microsoft .NET EventHandler delegate for strongly typed events without breaking syntax barriers. Here again, the whole premise of the original .NET design is that type casting and boxing is the easy answer for managing arguments everywhere.

But it is not a silver bullet and it is certainly not the optimal way of managing interfaces.

The fact is, you can pass anything you want into your event handlers. If you want your event handler to look like this:

delegate int SomethingHappened(MyClass sender, string someString, int someInt, List<Dah> dahs);

.. instead of this ..

delegate void SomethingHappened(object sender, SomethingHappenedEventArgs e);

.. you can.

Why would you want to? Well, for one thing, using EventArgs requires you to instantiate an EventArgs object every single time you raise this event. You could use a singleton or something but that would be atrocious design and not thread-safe.

You do not need to return void.

Some people might not actually even know this, but returning void is optional. You can return a string, or anything else. For example, if an event was named "FindAString" that expected a string as a return value, the event handler can return the string value to the event raiser.

public delegate string FindAStringHandler(object someContext);

public event FindAStringHandler FindAString;

Now here your class having the event FindAString can simply invoke the event as if it was a method.

string foundString = FindAString(this);

Obviously this will fail if FindAString was not subscribed to, but if it was, it would "just work". (If it wasn't, it would raise a NullReferenceException, which incidentally is a very poor exception choice on Microsoft's part!)

Be Wary Of Multiple Subscribers

One concern to have about returning anything other than void is that an event might have multiple subscribers. When there are multiple subscribing event handlers, the last event handler's return value is captured in the normal invocation.

For this reason, I wish that Microsoft would add a keyword on event syntax in C#: single. Some other verbiage but with the same function would be fine but the idea is that if at compile-time two event subscriptions were attempted on the same object's event at the same time in the same project then a compile-time error occur, and then at runtime if two event subscriptions were made from disperate projects on the same object's event then a runtime error would occur.

Event subscriptions are stacked like a list. They're not significantly unlike List<Delegate>, although technically that's not how they're implemented.

You can evaluate the number of subscribers by accessing the GetInvocationList().Length property of the event from the scope of the object that invokes/contains it.

public event FindAStringHandler FindAString;

public string Invoke() {
    if (FindAString != null) {
        if (FindAString.GetInvocationList().Length > 1)
            throw new InvalidOperationException("Too many subscribers to FindAString event.");
        else return FindAString(this);

    } else throw new InvalidOperationException("FindAString event not handled.");

}

This in effect manually enforces the single keyword functionality I proposed above.

That said, if you want the multiple subscribers to execute, such as to obtain the return values from each of the multiple subscribers, you can simply invoke each delegate in GetInvocationList(), one at a time.

//return FindAString(this, _params);  // returns one string
List<string> s = new List<string>();
FindAString.GetInvocationList().ToList().ForEach(delegate(Delegate d)
{
    s.Add(d.DynamicInvoke(this, _params) as string);
});
return s; // returns a list of string rather than just one string
 

This introduces kludginess, I suppose. That's about three or four more lines of code than I want to write. (Yes, I just said that! Keep in mind, using events everywhere like I'm inferring here would want an absolute minimum of code to be written for frequently needed patterns.) But then that's what utility classes and other patterns are for. You can create a generic (<T>) handler to wrap this functionality.

Interfaces Can Be Eventful, Too!!

One should not forget that interfaces can also define events. You can enforce the proper exposure of an event in an implemented object and have it use the correct signature (the correct delegate).

The only caveat I can think of with regard to interfaces and events is that there is no way to use interfaces to force an exposed event to be subscribed at compile-time; you can only enforce event handling by late-checking the delegates list at runtime, i.e. checking to see if it's null.

Putting It Together

So if IoC is handled by hand using events and event handlers, where does it go? The simple answer is on the controller. Consider the classic MVC scenario. Where else would it go? I can't imagine. MVC and event delegation are already pretty much built for each other.

In my mind, a mapping class the same as one that would be used with any third party IoC framework can be used to join the event-raising objects with their event handlers. It might optionaly reference a config file to produce the mappings, or not, whatever.

 

This diagram might be trash but I suppose it's something for the sake of discussion. Maybe consider the ability to swap out the controller with a mock controller as well. *shrug*

I decided to slap on ": Part 1" at the end of this blog entry's title because I still want to prove out how simple events and event handling can pull off the requirements of most of the standard testability scenarios that people using these other frameworks are using. But for the most part I *think* that this is a reasonable notion: Let's go back to the drawing board and simplify our codebases. What is it we're trying to do? What does C# not already have that cannot be enforced with some simple patterns already made available since C# was concieved?

kick it on DotNetKicks.com

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , , , , , ,

Software Development | C#

Comments

Add comment


(Will show your Gravatar icon)  

  Country flag

biuquote
  • Comment
  • Preview
Loading




 

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Jon Davis (aka "stimpy77") has been a programmer, developer, and consultant for web and Windows software solutions professionally since 1997, with experience ranging from OS and hardware support to DHTML programming to IIS/ASP web apps to Java network programming to Visual Basic applications to C# desktop apps.
 
Software in all forms is also his sole hobby, whether playing PC games or tinkering with programming them. "I was playing Defender on the Commodore 64," he reminisces, "when I decided at the age of 12 or so that I want to be a computer programmer when I grow up."

Jon was previously employed as a senior .NET developer at a very well-known Internet services company whom you're more likely than not to have directly done business with. However, this blog and all of jondavis.net have no affiliation with, and are not representative of, his former employer in any way.

Contact Me 


Tag cloud

Calendar

<<  December 2020  >>
MoTuWeThFrSaSu
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910

View posts in large calendar