Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generic EventHandler/EventArgs with a Generic Implicit Cast Operator

0.00/5 (No votes)
22 Sep 2015 1  
Generic EventHandler/EventArgs with a generic implicit cast operator

Introduction

This is an idea I had a few days back. While most of you out there will know how to create and use a generic EventHandler and/or generic EventArgs, it's that spicy little implicit cast operator that made me write this down for you.

I hope you find a little bit of useful information here.

Please leave me a note telling me if you already had the same idea that's shown here.

Background

We all have created EventHandlers and have derived EventArgs for lots of different reasons. I have used a generic implementation for years but was not really happy with the code, how it looked like... There were just too many brackets involved, especially when raising the event with a line like:

OnMyEvent(new GenericEventArgs<Dictionary<int, List<string>>>(myDic));

It would have been much nicer to raise the event just with:

OnMyEvent(myDic);

YES of course, I could just create an On... method that takes the Dictionary directly, but I didn't want that, as the protected virtual void OnEventName(EventArgs e) method signature is a standard, and I don't like to break standards.

That's how I came to the idea. I will show you now.

Create a Generic EventHandler and EventArgs

Let's start with a generic EventHandler and EventArgs that will allow us to forward one single Data Item with the event. This is enough for most cases, as we can easily deliver a class or data holder of any kind with all the details that need to be delivered with the event.
(Comments are skipped here but in the downloadable file, everything is XML-commented of course):

public class ItemEventArgs<T> : EventArgs
{
    public ItemEventArgs(T item)
    {
        Item = item;
    }
 
    public T Item { get; protected set; }
}

The EventHandler for this is simple and straightforward, too:

public delegate void ItemEventHandler<T>(object sender, ItemEventArgs<T> e);

So now, we can declare any event that shall deliver one single item of data/information as follows (I take this from a real code where I use that delegate in a UDP communication class):

public event ItemEventHandler<DataPacket> PacketReceived;

and the protected method to raise the event is completely standard, too:

protected virtual void OnPacketReceived(ItemEventArgs<DataPacket> e)
{
    PacketReceived?.Invoke(this, e);
}

From my code, I could raise the event now as:

OnPacketReceived(new ItemEventArgs<DataPacket>(_packet));

It's this new ItemEventArgs<DataPacket> that I didn't like too much, I preferred a call like:

OnPacketReceived(_packet);

without having to declare a On... method that takes a packet directly. So I tried an implicit cast operator, wondering if this would work generic. And it does!
So, if you never did a cast operator like this, it will be new information for you, and I really had an "ahaaaaa!" effect when I found that out, and this is the reason for sharing it with you.

I put that cast operator in the ItemEventArgs<T> class, and it did what I was really hoping for:

public static implicit operator ItemEventArgs<T>(T item)
{
    return new ItemEventArgs<T>(item);
}

This little gem allowed me, from now on to call any of my On... methods that work with the generic ItemEventargs to be called directly with the object to deliver, and to skip the new ItemEventArgs<T>(...) from now on.
The raising code in my UDP class (and many other classes) now really looks like:

OnPacketReceived(_packet);

without ever having to declare another On... method that would break what is considered as "standard". Inheritors still find their common expected virtual method to override, but the callers can use it in a more readable way.

This generic implicit cast is not limited by the virtual On... methods. ANY call, that expects ItemEventArgs<T> can now be done directly without having to instantiate the <T> class! You can do implicit generic casts like that for any class you make. Hope this gives you a new idea or two!

I discussed this with some colleagues here in the office and they all liked the idea because the code is more beautiful to read.

So if you didn't have the idea of a generic implicit cast operator so far, I hope you will take this for your own use.

For easier copying of the code, here it is, all in a single block:

using System;

namespace Mbi.Toolbox
{
    /// <summary>
    /// Generic EventHandler to be used with the generic 
    /// EventArgs class <see cref="ItemEventArgs{T}"/>.
    /// </summary>
    /// <typeparam name="T">The item type contained 
    /// in the <see cref="ItemEventArgs{T}"/>.</typeparam>
    /// <param name="sender">The sender of the event.</param>
    /// <param name="e">The 
    /// <see cref="ItemEventArgs{T}"/> instance containing the event data.</param>
    public delegate void ItemEventHandler<T>(object sender, ItemEventArgs<T> e);

    /// <summary>
    /// Generic EventArgs for Events that need to supply one parameter. 
    /// For use with the generic EventHandler <see cref="ItemEventHandler{T}"/>.
    /// </summary>
    /// <typeparam name="T">The type of the parameter used in the EventArgs.</typeparam>
    public class ItemEventArgs<T> : EventArgs
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="ItemEventArgs{T}"/> class.
        /// </summary>
        /// <param name="item">The item to passed with the event.</param>
        public ItemEventArgs(T item)
        {
            Item = item;
        }

        /// <summary>
        /// Gets the item passed with this event.
        /// </summary>
        /// <value>
        /// The item passed with this event.
        /// </value>
        public T Item { get; protected set; }

        /// <summary>
        /// Implicitly converts an item of Type T to <see cref="ItemEventArgs{T}"/>.
        /// </summary>
        /// <param name="item">The item to convert.</param>
        /// <returns>
        /// A new instance of <see cref="ItemEventArgs{T}"/> containing the item.
        /// </returns>
        public static implicit operator ItemEventArgs<T>(T item) => new ItemEventArgs<T>(item);
    }
}

History

  • 2013-01-11 Published
  • 2015-09-22 Updated the event invoke to the new C# 6.0 syntax

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here