Tags: , , | Categories: Code Development, Generics Posted by nurih on 2/14/2007 3:29 PM | Comments (0)

When you want to create custom events, often you need to pass an event argument, and as often you need only to pass in one parameter - your object.

 

So what you used to do is:

    public event MyCustomEventHandler MyCustomEvent;
public delegate void MyCustomEventHandler(MyCustomEventArg evt);
public class MyObject
{
public string foo;
public int count;
public DateTime when;
public MyObject()
{
foo = "hello world";
count = 42;
when = DateTime.Now;
}
public override string ToString()
{
return string.Format("{0}\t{1}\t{2}",foo,count,when);
}
}
    public class MyCustomEventArg : EventArgs
{
private MyObject _value;
public MyObject Data
{
get { return _value; }
set { _value = value; }
}
public MyCustomEventArg(MyObject value)
{
_value = value;
}
}
 

The not so pretty thing about it is that you had to mind-numbingly create a class which derives from EventArgs just to be able to pass your single event argument MyObject.

Now come generics and Microsoft has provided the generic type:

EventHandler<TEventArgs>
and with it, you can now code
 
   public event EventHandler<MyCustomEventArgs> MyCustomEvent;
// Not "necessary" anymore..
// public delegate void MyCustomEventHandler(MyCustomEventArg evt);
public class MyObject
{...}
public class MyCustomEventArg : EventArgs
{
private MyObject _value;
public MyObject Data
{
get { return _value; }
set { _value = value; }
}
public MyCustomEventArg(MyObject value)
{
_value = value;
}
}
 

Well, that saved me one line of code! I can ditch work early today :-)

Wouldn't it by nice if I could cut out the whole MyCustomEventArg class? that would save some more lines of code,

and prevent my head from hitting the desk and busting my skull on an upside-down thumb tack.

Well, that's pretty easy to do: create a new class using generics. It supports a single object as a parameter which

can be passed in at construction.

 
using System;
/// <summary>
/// Encapsulates a single object as a parameter for simple event argument passing
/// <remarks>Now you can declare a simple event args derived class in one wrap</remarks>
/// <code>public void delegate MyCustomeEventHandler(TypedEventArg&lt;MyObject&gt; theSingleObjectParameter)</code>
/// </summary>
public class TypedEventArg<T> : EventArgs
{
private T _Value;
public TypedEventArg(T value)
{
_Value = value;
}
public T Value
{
get { return _Value; }
set { _Value = value; }
}
}

Now the code can look like

 

    public event MyCustomEventHandler MyCustomEvent;
public delegate void MyCustomEventHandler(TypedEventArg<MyObject> evt);
public class MyObject
{ ... }
    //This whole thing now goes away.. 
//public class MyCustomEventArg : EventArgs
//{
//    private MyObject _value;
//    public MyObject Data
//    {
//        get { return _value; }
//        set { _value = value; }
//    }
//    public MyCustomEventArg(MyObject value)
//    {
//        _value = value;
//    }
//}

And life is good. I do have to declare the delegate though, so you daring enough to type 2 nested angled brackets, you can go for the gold:

    public event EventHandler<TypedEventArg<MyObject>> MyCustomEvent;
//public delegate void MyCustomEventHandler(TypedEventArg<MyObject> evt);
public class MyObject
{ ... }
 

This lets you dispense with 1 extra line of code so for those of us typing with 1 or 2 fingers, life is better. Readability of nested generic references vs. the declaration of a delegate is a matter of taste mostly.

Taste would also guide you as to whether you like the declaration on the event consumer side better:

In case of a full delegate declaration:

            Eventful eventSource = new Eventful();
eventSource.MyCustomEvent += new Eventful.SomethingHappenedEventHandler(OnSomethingHappenedEvent);

But if a generics EventHandler is used:

            eventSource.MyCustomEvent += new EventHandler<TypedEventArg<MyObject>>(classWithEvent2_SomethingHappendedEvent);

In conclusion, by creating a simple generic type around EventArgs, I can save a few keystrokes and learn something while at it. Left as a future excercise : look at adding a constraint to the argument type such that the argument type is serializeable.