(+N) Consulting Inc.

Consulting. Software solutions. Training.

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