Generics - delegate and custom event args

Wednesday, February 14, 2007 3:29 PM

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<MyCustomEventArg> 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.



  • Share This Post:
  • Share on Twitter
  • Share on Facebook
  • Share on Technorati

Feedback

# re: Generics - delegate and custom event args

In the second code block,

public event EventHandler<MyCustomEventHandler> MyCustomEvent;

should be

public event EventHandler<MyCustomEventArg> MyCustomEvent;
9/25/2009 8:17 AM | zootius

Post a comment