Daniel Mošmondor
Daniel Mošmondor

Reputation: 19976

Packing event arguments in a class, why?

Most .NET stock events are have this signature:

delegate void SomethingSomething(SomethingEventArgs e);
event SomethingSomething OnSomethingSomething;

and

class SomethingEventArgs
{
    public string Name;
    public int Index;
    public double Groar;
}

Why is that better (obviously is, otherwise anyone would choose to do) than:

delegate void SomethingSomething(string Name, int Index, double Groar);
event SomethingSomething OnSomethingSomething;

since you don't have to pack your parameters to an object, and without initializers (.NET 2.0) it was kind of typing exercise.

One reason that comes to mind is that you can return your values simpler when having them packed in an object - ie. handler can modify a member of the object. However, with multicast events, that can't always be good anyway.

So, why?

Upvotes: 24

Views: 2916

Answers (9)

Bas Slagter
Bas Slagter

Reputation: 9929

The main reason is that it is more maintainable. If you pass an objects and any of the properties change, you only have to modify that. If you pass variables, that is a lot more work. So, the code gets more maintainable and more readable this way.

A quote from Microsoft's Code Complete:

Limit the number of a routine’s parameters to about seven. Seven is a magic number for people’s comprehension. Psychological research has found that people generally cannot keep track of more than about seven chunks of information at once (Miller 1956). This discovery has been applied to an enormous number of disciplines, and it seems safe to conjecture that most people can’t keep track of more than about seven routine parameters at once.

In practice, how much you can limit the number of parameters depends on how your language handles complex data types. If you program in a modern language that supports structured data, you can pass a composite data type containing 13 fields and think of it as one mental “chunk” of data. If you program in a more primitive language, you might need to pass all 13 fields individually,

If you find yourself consistently passing more than a few arguments, the coupling among your routines is too tight. Design the routine or group of routines to reduce the coupling. 1f you are passing the same data to many different routines, group the routines into a class and treat the frequently used data as class data.

Quoted text from the original post's image

Upvotes: 15

Kevin McCormick
Kevin McCormick

Reputation: 2398

Firstly, the reason why this pattern is so common (as you have asked) is because Microsoft has specifically prescribed so when they developed the Event Pattern (yes they coined this term, too). I don't necessarily think this is better or worse than coming up with your own delegate signatures, but following a well-known convention can have its advantages.

Per Microsoft:

  • The return type is Void.

  • The first parameter is named sender and is of type Object. This is the object that raised the event.

  • The second parameter is named e and is of type EventArgs or a derived class of EventArgs. This is the event-specific data.

  • The method takes exactly two parameters.

To get more at the point of your question, though, I think the rationale for using EventArgs is twofold:

  1. So that classes that inherit from your class can still raise the event, with a derived EventArgs class.
  2. So that classes that handle your event can use common event handlers to handle several types of events, even if different events use different EventArgs. Or, if you change the event down the road, any classes that already handle the event don't need to change their code because the handlers will still be compatible with the new event.

For further reading, see more info from Microsoft:

  1. More info about how to design events: http://msdn.microsoft.com/en-us/library/ms229011.aspx
  2. A detailed how-to on using this pattern: http://msdn.microsoft.com/en-us/library/w369ty8x.aspx

Upvotes: 3

Trevor Elliott
Trevor Elliott

Reputation: 11252

In addition to the backward-compatibility convention that others have already mentioned, the EventArgs class enables more control over how the parameters are contiguously accessed as they are passed to more than one object.

This control can be used to make certain values immutable, so that if an event is sent to 3 different subscribers you can guarantee it's not tampered with.

At the same time you can offer multiple [out] values, and you don't have to juggle return values or [ref] parameters, which significantly complicates the multicasting process. It also ensures that any validation logic occurs in the EventArgs subclass, NOT in the class that fires the events.

Upvotes: 1

Anders Abel
Anders Abel

Reputation: 69280

As others have pointed out, there are maintainability and concistency reasons for this. The EventArgs approach also makes it possible for the event handler to modify the EventArgs.

One reason for modifying the EventArgs is errorhandling. An exception caught somewhere on a background thread is communicated to the client as an event. The client can set a flag in the EventArgs to indicate the exception was handled an shouldn't be rethrown on the background thread.

Another example is the ObjectDataSource class that lets the client supply an object instance when one is required. This is done by subscribing to the ObjectDataSource.ObjectCreating event and supplying the object instance by setting a member of the EventArgs.

Upvotes: 6

Gabriel McAdams
Gabriel McAdams

Reputation: 58301

Using a class allows the event's subscribers to effect the outcome.

When you pass an instance of a class as a parameter to a method, it is passed by reference. This allows the object instance to change during the method call. Those changed values can then be read by the caller after the event is raised.

For instance:

Look at the FormClosingEventHandler Delegate (in Windows Forms).

This delegate uses a parameter of type FormClosingEventArgs.

If a subscriber to an event using this delegate sets the Cancel property (inherited by CancelEventArgs) to true, then the form is not closed.

In addition, these answers are also correct:

  1. jgauffin
  2. Baszz

Upvotes: 3

Richard
Richard

Reputation: 109100

The benefit is the pattern; and having a pattern gives both consistency and the the ability to use other APIs across multiple event types:

  • The EventHandler<T> delegate type (you don't need to define your own delegate type).
  • The Reactive Extensions (Rx) have conversion of an event into an IObservable<T> allowing use of LINQ on event sources with Observable.FromEvent.

Also you've got the signatures wrong:

  • The delegate takes two arguments: object source and SomethingEventArgs
  • The SomethingEventArgs type inherits EventArgs.

Thus your code should be, to be an exemplar of the pattern:

At namespace scope:

public class SomethingEventArgs : EventArgs {
    public string Name;
    public int Index;
    public double Groar;
}

public delegate void SomethingSomething(object source, SomethingEventArgs e);

and in the type exposing the type

public event SomethingSomething OnSomethingSomething;

(An event could also be internal.)

Upvotes: 9

Steven Muhr
Steven Muhr

Reputation: 3379

In order to follow the .net standards, the recommanded way to create an event is:

  1. Create a class that inherits from EventArgs
  2. Use the EventHandler<T> generic delegate

As doing so reduces the amount of work required to subsequently change the number and types of values sent to event subscribers.

Example:

public event EventHandler<SometingEventArgs> SomethingEvent;

class SomethingEventArgs 
{     
      public string Name;    
      public int Index;     
      public double Groar; 
}

Upvotes: 1

Lukazoid
Lukazoid

Reputation: 19426

The reason for this is to avoid breaking changes. For example, your class may wish to include further information with it's event, however every thing which used that event would break, as the delegate no longer matched. By having a strict delegate, your event can encapsulate more information in the future without affecting any subscribers.

Edit: As per the comments I'll expand on how this affects the reduction of breaking changes.

If we wished to add further information to our raised event, by using a single class derived from EventArgs new properties/methods can be added. This will mean any existing subscribers to the event will require no change, as the addition of these properties does not affect them. The only required change would be where these properties are set/used, e.g. where the event is raised.

Upvotes: 11

jgauffin
jgauffin

Reputation: 101166

Read about Open/Closed principle.

By using a class, all inherited classes can introduce extra functionality without having to change the delegate signature. They can simply introduce a new event class (ExtendedEventArgs) which inherits yours (SomeEventArgs).

Upvotes: 31

Related Questions