92

I am getting ready to create a generic EventArgs class for event args that carry a single argument:

public class EventArg<T> : EventArgs
{
    // Property variable
    private readonly T p_EventData;

    // Constructor
    public EventArg(T data)
    {
        p_EventData = data;
    }

    // Property for EventArgs argument
    public T Data
    {
        get { return p_EventData; }
    }
}

Before I do that, does C# have the same feature built in to the language? I seem to recall coming across something like that when C# 2.0 came out, but now I can't find it.

Or to put it another way, do I have to create my own generic EventArgs class, or does C# provide one? Thanks for your help.

David Veeneman
  • 18,912
  • 32
  • 122
  • 187
  • 9
    You may have seen `Eventhandler` – H H Jul 22 '10 at 18:33
  • 1
    You may have seen ```EventArgs``` in CAB/SCSF-based code. It's quite common in CAB/SCSF applications. While it's not part of the framework, there *is* an EventArgs implementation. – Shaun Wilson Feb 26 '13 at 19:49

7 Answers7

71

No. You probably were thinking of EventHandler<T>, which allows you to define the delegate for any specific type of EventArgs.

I personally don't feel that EventArgs<T> is quite as good of a fit, though. The information used as a "payload" in the event args should be, in my opinion, a custom class to make its usage and expected properties very clear. Using a generic class will prevent you from being able to put meaningful names into place. (What does "Data" represent?)

Pang
  • 9,564
  • 146
  • 81
  • 122
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 57
    In data-centric, MPI-like applications it is quite common to see an EventArgs in place for data processing so that developers can leverage an in-built, type-safe subscription/registration system (.NET Events and Delegates), as it makes absolutely no sense to create EventArgs subclasses just to transport data. If your intent is to transport data, arguably it makes more sense to define an EventArgs you can subclass for discrete uses WITHOUT regard for the data being transported. TL:DR This first part of this answer is factful and accurate, the second part is subjective/opinion. – Shaun Wilson Feb 26 '13 at 19:31
  • 1
    I suppose you're right... resist the laziness that got me here. It's like when lazy developers use Tuple<,>. Terrible stuff. – CRice Aug 01 '14 at 03:35
  • A good point, but this would essentially be doing the same thing as Microsoft did when they added the Tag property to the Controls classes. Of course, just coz' MS did it, doesn't make it right. – Ken Richards Nov 20 '14 at 19:23
  • 1
    I agree with the first commentator, the whole second half of this answer is a subjective opinion / code-religion that was not constructive. :( – BrainSlugs83 Sep 06 '18 at 08:08
  • 12
    How does **EventHandler** or **EventHandler** convey any less information than **CustomerEventHandler** or **OrderEventHandler**? – Quark Soup Dec 12 '18 at 19:48
  • The benefits to a custom EventArgs class are API compatibility and maintainability. If you need to change the arguments that are passed, you only have to change the EventArgs class. This means that adding a member to the EventArgs class would be a **non-breaking** change to the API. If you use a generic class instead, changes to the arguments would change the event / handler signatures, and would be a **breaking change** to the API and could result in substantial code changes. The generic version is easier when this is not a concern, and I think there should be one in the BCL. – Robear May 01 '19 at 13:08
37

I must say I don't understand all the 'purists' here. i.e. if you already have a bag class defined - which has all the specifics, properties etc. - why the hack create one extra unnecessary class just to be able to follow the event/args mechanism, signature style? thing is - not everything that is in .NET - or is 'missing from' for that matter - is 'good' - MS's been 'correcting' itself for years... I'd say just go and create one - like I did - cause I needed it just like that - and saved me lot of time,

NSGaga-mostly-inactive
  • 14,052
  • 3
  • 41
  • 51
  • 3
    In this case, the 'purists' point is to make intention as clear as possible. In a production app, I have dozens of these EventArgs classes. A year from now, it will be easier to understand what I did if I follow Reed's advice and create a custom EventArgs class. – David Veeneman Mar 27 '11 at 15:13
  • 9
    did not mean to label anybody :) point is in the 'generic' in the event - if you just need a dozen different events, ok. But if you don't know the nature of the T beforehand - that is only known at runtime - well, you kind of need a generic event. So, if you have an app that is a good part dealing w/ generics, an arbitrary # of types, unknown - then you also need generic events. or i.e. there's no clean-cut solution for every problem, rule of thumb is just a rule of thumb - and that's what I meant by purists, each solution is different, you need to weigh the things, pros and cons – NSGaga-mostly-inactive Apr 08 '11 at 10:37
  • 2
    Design isn't black and white, so I tend to agree with NSGaga here. Sometimes, "quick and dirty" is appropriate. The language should be feature-rich to suit most use-cases. It should be up to the developer to decide what's appropriate. The last time MS tried to force "good" design by limiting the language was WPF when they tried to force the use of XAML by severely handicapping the .NET API, and it was terrible. – Robear May 27 '18 at 15:53
10

It does exist. At least, it does now.

You can find DataEventArgs<TData> in some different Microsoft assemblies/namespaces, for instance Microsoft.Practices.Prism.Events. However these are namespaces that you might not find natural to include in your project so you might just use your own implementation.

Ulf Åkerstedt
  • 3,086
  • 4
  • 26
  • 28
  • I've been trying to find recommendations and considerations regarding when and why to use such a generic EventArgs class. See also: http://stackoverflow.com/questions/15766219/ok-to-use-dataeventargstdata-instead-of-customized-event-data-class – Ulf Åkerstedt Apr 09 '13 at 08:22
  • Here are some aspects to beware of when deciding on using a generic EventArgs: http://stackoverflow.com/questions/129453/net-eventhandlers-generic-or-no/129613#129613 – Ulf Åkerstedt Oct 03 '13 at 14:23
8

In case you choose not to use Prism, but still would like to try a generic EventArgs approach.

public class GenericEventArgs<T> : EventArgs
{
    public T EventData { get; private set; }

    public GenericEventArgs(T EventData)
    {
        this.EventData = EventData;
    }
}

// Use the following sample code to declare ObjAdded event

public event EventHandler<GenericEventArgs<TargetObjType>> ObjAdded;

// Use the following sample code to raise ObjAdded event

private void OnObjAdded(TargetObjType TargetObj)
{
    if (ObjAdded!= null)
    {
        ObjAdded.Invoke(this, new GenericEventArgs<TargetObjType>(TargetObj));
    }
}

// And finnaly you can subscribe your ObjAdded event

SubscriberObj.ObjAdded +=  (object sender, GenericEventArgs<TargetObjType> e) =>
{
    // Here you can explore your e.EventData properties
};
Community
  • 1
  • 1
Julio Nobre
  • 4,196
  • 3
  • 46
  • 49
4

THERE IS NO BUILT-IN GENERIC ARGS. If you follow Microsoft EventHandler pattern, then you implement your derived EventArgs like you suggested: public class MyStringChangedEventArgs : EventArgs { public string OldValue { get; set; } }.

HOWEVER - if your team style guide accepts a simplification - your project can use a lightweight events, like this:

public event Action<object, string> MyStringChanged;

usage :

// How to rise
private void OnMyStringChanged(string e)
{
    Action<object, string> handler = MyStringChanged;    // thread safeness
    if (handler != null)
    {
        handler(this, e);
    }
}

// How to handle
myObject.MyStringChanged += (sender, e) => Console.WriteLine(e);

Usually a PoC projects use the latter approach. In professional applicatons, however, be aware of FX cop justification #CA1009: https://msdn.microsoft.com/en-us/library/ms182133.aspx

epox
  • 9,236
  • 1
  • 55
  • 38
1

The problem with a generic type is that even if DerivedType inherits from BaseType, EventArgs(DerivedType) would not inherit from EventArgs(BaseType). Using EventArgs(BaseType) would thus prevent later using a derived version of the type.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • 3
    This is patently false. please see "Covariance and Contravariance in Generics - Microsoft.com" http://msdn.microsoft.com/en-us/library/dd799517.aspx – Shaun Wilson Feb 26 '13 at 19:33
  • 1
    @ShaunWilson: What aspect is false? One could define a covariant interface `IEventArgs`, but the only classes which can be generic with regard to generic type parameters are delegates that will not be fed to `Delegate.Combine`. Delegates that will be used with `Delegate.Combine` should not be covariant, since one may store e.g. an `Action` into a field of type `Action`, but a later attempt to `Combine` that with an instance of `Action` will fail. – supercat Feb 26 '13 at 21:53
-4

The reason this does not exist is because what would end up happening is you implement this, and then when you go to fill in the T you should create a class with strongly typed unambiguous properties that acts as the data bag for your event arg, but halfway through implementing that you realize there's no reason you don't just make that class inherit from EventArgs and call it good.

Unless you just want a string or something similarly basic for your data bag, in which case there are probably EventArgs classes standard in .NET which are meant to serve whatever simple purpose you're getting at.

Jimmy Hoffa
  • 5,909
  • 30
  • 53
  • 1
    -1. I have EXACTLY this sitaution now, and my payload would be an immutable object that is a "message" coming from a bus... and isu sed in other places, too. The custom EventArgs.... creates a redundant class here. Unusual, but the application is so. – TomTom Jul 22 '10 at 21:04