+N Consulting, Inc.

Generics - delegate and custom event args

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; set;}
}

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; set; }
//
// 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 MyObject Data { get; set; }

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.

Notice

We use cookies to personalise content, to allow you to contact us, to provide social media features and to analyse our site usage. Information about your use of our site may be combined by our analytics partners with other information that you’ve provided to them or that they’ve collected from your use of their services. You consent to our cookies if you continue to use our website.