Click here to Skip to main content
15,881,204 members
Articles / Programming Languages / C#
Tip/Trick

Generic EventHandler/EventArgs with a Generic Implicit Cast Operator

Rate me:
Please Sign up or sign in to vote.
4.82/5 (12 votes)
22 Sep 2015CPOL3 min read 45.2K   134   21   12
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:

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

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

C#
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):

C#
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:

C#
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):

C#
public event ItemEventHandler<DataPacket> PacketReceived;

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

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

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

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

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

C#
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:

C#
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:

C#
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:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Austria Austria
Software Developer since the late 80's, grew up in the good old DOS-Era, switched to windows with Win95 and now doing .net since early 2002 (beta).
Long year c# experience in entertainment software, game programming, directX and XNA as well as SQLServer (DBA, Modelling, Optimizing, Replication, etc) and Oracle Databases in Enterprise environments. Started with Android development in 2014.

Developer of the gml-raptor platform (See my github profile below).

My Game Developer Profile at itch.io
My Repositories at github

Comments and Discussions

 
Questiongood article ! ... one question Pin
BillWoodruff23-Sep-15 20:59
professionalBillWoodruff23-Sep-15 20:59 
AnswerRe: good article ! ... one question Pin
Mike (Prof. Chuck)24-Sep-15 7:56
professionalMike (Prof. Chuck)24-Sep-15 7:56 
QuestionNice touch Pin
dazinator23-Sep-15 14:06
dazinator23-Sep-15 14:06 
AnswerRe: Nice touch Pin
Mike (Prof. Chuck)23-Sep-15 18:25
professionalMike (Prof. Chuck)23-Sep-15 18:25 
QuestionCannot compile using C# in VS 2013 Pin
BillKru23-Sep-15 10:53
BillKru23-Sep-15 10:53 
AnswerRe: Cannot compile using C# in VS 2013 Pin
Mike (Prof. Chuck)23-Sep-15 18:23
professionalMike (Prof. Chuck)23-Sep-15 18:23 
GeneralRe: Cannot compile using C# in VS 2013 Pin
BillKru23-Sep-15 21:46
BillKru23-Sep-15 21:46 
Question.Net EventHandler<T> Pin
bcd21-Sep-15 11:33
bcd21-Sep-15 11:33 
AnswerRe: .Net EventHandler<T> Pin
Mike (Prof. Chuck)21-Sep-15 18:33
professionalMike (Prof. Chuck)21-Sep-15 18:33 
GeneralRe: .Net EventHandler<T> Pin
Johnny Two Tills1-Jan-16 3:58
Johnny Two Tills1-Jan-16 3:58 
GeneralMy vote of 5 Pin
ishihidul14-Jan-13 12:05
ishihidul14-Jan-13 12:05 
GeneralRe: My vote of 5 Pin
Mike (Prof. Chuck)14-Jan-13 18:52
professionalMike (Prof. Chuck)14-Jan-13 18:52 

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.