Click here to Skip to main content
Click here to Skip to main content

Generic Event Arguments

, 5 Oct 2009
Rate this:
Please Sign up or sign in to vote.
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:

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>

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>

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

So the declaration of the class above becomes:

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>

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>

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:

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:

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)

About the Author

DaveyM69
CEO Dave Meadowcroft
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralTotally useless PinmemberTheSebaster13-Oct-09 9:55 
GeneralRe: Totally useless PinmvpDaveyM6913-Oct-09 10:48 
GeneralRe: Totally useless PinmemberTheSebaster14-Oct-09 2:04 
GeneralRe: Totally useless PinmvpDaveyM6914-Oct-09 4:20 
GeneralRe: Totally useless PinmemberTheSebaster14-Oct-09 5:55 
Well my point is: if you use int as and the object that provide
the event has 20 subscribers and for a reason you need to pass a float
and a string in addition to the int, you will have to create a Complex
object including Int, Float and String (CplxIntFloatString) and then replace
all 20 subscribers method callback prototype from EventArgs to EventArgs even if 19 subscribers don't need the float and the string.
GeneralRe: Totally useless PinmvpDaveyM6914-Oct-09 6:40 
GeneralExcellent! Pinmemberdshorter113-Oct-09 8:30 
GeneralRe: Excellent! PinmvpDaveyM6913-Oct-09 9:07 
GeneralMy vote of 1 PinmemberSteve Solomon12-Oct-09 23:04 
GeneralThe very first sentence addresses your point. PinmvpDaveyM6913-Oct-09 1:48 
JokeBut... PinmemberPIEBALDconsult6-Oct-09 5:10 
GeneralRe: But... PinmvpDaveyM696-Oct-09 6:15 
GeneralNice! Pinmemberring_05-Oct-09 18:55 
GeneralRe: Nice! PinmvpDaveyM695-Oct-09 22:45 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140721.1 | Last Updated 5 Oct 2009
Article Copyright 2009 by DaveyM69
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid