Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C# 4.0

Generic Event Arguments

Rate me:
Please Sign up or sign in to vote.
4.60/5 (24 votes)
5 Oct 2009CPOL3 min read 47.7K   274   46   14
Save your keyboard and fingers, and write less code using generic event arguments.

Introduction

Let me start by saying that this idea isn't new, but I constantly find myself using this code and I couldn't find a decent example on CodeProject, so here it is for anyone that wants/needs it. Also, as the principle is very simple, this is not going to be the longest article in history!

Microsoft introduced a few generic delegates that are commonly used in .NET 2.0 such as EventHandler<TEventArgs>, but for some reason didn't include a generic version of EventArgs itself.

We are all familiar with event arguments that get passed by objects in our code. They are a very useful way of sending values between classes. The problem is that every time we want to pass a different type of value, we need to create a new class that derives from System.EventArgs (or a class that already does this).

Normal EventArgs

For example, if we wanted to pass an int from a class in our code, we would need to write a class such as this:

C#
public class IntEventArgs : EventArgs
{
    public IntEventArgs(int value)
    {
        Value = value;
    }
    
    public int Value { get; private set; }
}

OK, so this is no problem and very trivial. If we need a similar class, but want to pass a string, then we have to write another class that is essentially the same, but using string in place of int. This is a waste of our time and exactly what Generics are there for!

Generic EventArgs

The same class using Generics looks like this:

EventArgs<T>

C#
public class EventArgs<T> : EventArgs
{
    public EventArgs(T value)
    {
        Value = value;
    }

    public T Value { get; private set; }
}

Now we can pas in any type we like in place of the int, without needing to write another class ever!

In the real world, there are many types of event arguments that could be suitable for applying Generics to. Because all our generic versions will have the same property, I use a very simple interface that all my generic event argument classes implement.

IEventArgs<T>

C#
public interface IEventArgs<T>
{
    T Value { get; }
}

So the declaration of the class above becomes:

C#
public class EventArgs<T> : EventArgs, IEventArgs<T>
{
    // ...
}

The next obvious candidate is System.ComponentModel.CancelEventArgs. The generic version of this derives from CancelEventArgs and IEventArgs. This class is only slightly complicated by the need for more than one constructor and calls to the base constructor.

CancelEventArgs<T>

C#
public class CancelEventArgs<T> : CancelEventArgs, IEventArgs<t>
{
    public CancelEventArgs(T value)
        : this(value, false)
    { }
    public CancelEventArgs(T value, bool cancel)
        : base(cancel)
    {
        Value = value;
    }

    public T Value { get; private set; }
}

I find the most common place I use cancel event arguments are in XxxChanging events, where it is useful to provide the current value, the new value, and the option to cancel, all in one event argument class instance. Instead of using a different generic EventArgs for this, it is just as easy to create a generic ChangingData class and use that as the type for our generic CancelEventArgs.

ChangingData<T>

C#
public class ChangingData<T>
{
    public ChangingData(T oldData, T newData)
    {
        OldData = oldData;
        NewData = newData;
    }
    
    public T OldData { get; private set; }
    public T NewData { get; private set; }
}

In Use

In use, an event declaration might look like this if the value that was changing was an int:

C#
public event EventHandler<CancelEventArgs<ChangingData<int>>> ValueChanging;

That's a generic event handler with a generic cancel event argument whose type is a generic class! The actual Value property implementation in such a class would probably look similar to this:

C#
public int Value
{
    get { return _Value; }
    set
    {
        if (_Value != value)
        {
            /* Create a new generic CancelEventArgs instance using a
            * generic ChangingData instance for the type */
            CancelEventArgs<ChangingData<int>> e =
                new CancelEventArgs<ChangingData<int>>(
                    new ChangingData<int>(_Value, value));
            // Raise the ValueChanging event passing the generic CancelEventArgs instance
            OnValueChanging(e);
            // Check the Cancel state. If false, do nothing
            if (!e.Cancel)
            {
                // Update _Value and raise the ValueChanged event
                _Value = value;
                OnValueChanged(new EventArgs<int>(_Value));
            }
        }
    }
}

In fact, this is pasted straight out of the source code attached.

Conclusion

So that's it, a very simple way to reduce the amount of code we have to write when creating events, by using Generics. The attached source code has fully commented versions of all the classes above as well as a working demonstration of them in use. I hope you find them useful as I do.

History

  • V1. 5th October 2009: Initial version.
  • V2. 5th October 2009: Minor fixes.
  • V3. 5th October 2009: Fixed multiple <>.
  • V4. 5th October 2009: Fixed more <> - damn MS for picking these for Generics!
  • V5. 5th October 2009: And one more!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
CEO Dave Meadowcroft
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralTotally useless Pin
TheSebaster13-Oct-09 9:55
TheSebaster13-Oct-09 9:55 
GeneralRe: Totally useless Pin
DaveyM6913-Oct-09 10:48
professionalDaveyM6913-Oct-09 10:48 
GeneralRe: Totally useless Pin
TheSebaster14-Oct-09 2:04
TheSebaster14-Oct-09 2:04 
GeneralRe: Totally useless Pin
DaveyM6914-Oct-09 4:20
professionalDaveyM6914-Oct-09 4:20 
GeneralRe: Totally useless Pin
TheSebaster14-Oct-09 5:55
TheSebaster14-Oct-09 5:55 
GeneralRe: Totally useless Pin
DaveyM6914-Oct-09 6:40
professionalDaveyM6914-Oct-09 6:40 
GeneralExcellent! Pin
dshorter113-Oct-09 8:30
dshorter113-Oct-09 8:30 
This clear and very useful example merits a 4 but I rate it a 5 to make up for any 1's submitted by professional poo-poo'ers
GeneralRe: Excellent! Pin
DaveyM6913-Oct-09 9:07
professionalDaveyM6913-Oct-09 9:07 
GeneralMy vote of 1 Pin
SteveTheThread12-Oct-09 23:04
SteveTheThread12-Oct-09 23:04 
GeneralThe very first sentence addresses your point. Pin
DaveyM6913-Oct-09 1:48
professionalDaveyM6913-Oct-09 1:48 
JokeBut... Pin
PIEBALDconsult6-Oct-09 5:10
mvePIEBALDconsult6-Oct-09 5:10 
GeneralRe: But... Pin
DaveyM696-Oct-09 6:15
professionalDaveyM696-Oct-09 6:15 
GeneralNice! Pin
ring_05-Oct-09 18:55
ring_05-Oct-09 18:55 
GeneralRe: Nice! Pin
DaveyM695-Oct-09 22:45
professionalDaveyM695-Oct-09 22:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.