Two questions regarding events - c#

I would like to know:
Subscriber is a name for class that subscribes for the event or also for the method that is handling the event? I mean, does it make sense to say "subscribe method to event"?
On MSDN it says that event delegate should have exactly 2 arguments. Not sure what it means as I often create events with my custom delegate that has e.g. none or one argument.

1) Yes, it definitely makes sense to talk about subscribing a method to an event. You can actually think of there being three entities involved:
The subscribing code which is actually performing the subscription itself
The event publisher
The handler which is being subscribed; this is often - but not always - code in the same class as the subscribing code
2) You certainly can create events using delegates with any number of parameters. The convention is that delegates used in events have two parameters: a "sender" and an "argument" derived from EventArgs. For example, this means that you can subscribe a handler from a method with a signature of void Foo(object sender, EventArgs e) to any event following the convention.

A subscriber is a method that is added as an event handler.
Standard practice is to use the EventHandler<T> delegate for events; this has two arguments.
However, you can create your own events with any number of arguments.

I'll concentrate on the second point. As Jon pointed out, you can use your own delegates for events. But you rarely should. Even if you don't care about conventions, look at it this way: whenever you use your own events with custom delegates, you have the following code besides the event field itself:
Your custom delegate
Event handlers (methods with matching signature) - sometimes a lot of them
Event invocation points (where you need to pass all the parameters)- sometimes a lot of them
I also tend to create the InvokeMyEvent methods that do the nullity checking and other stuff that needs to be done
You have to change all of these if you decide to add or remove a parameter or change a parameter type.
Now, if you create your own class that inherits EventArgs or use the EventArgs<T>, you have:
Your CustomEventArgs class
Event handlers
Event invocation points
InvokeMyEvent methods.
Whenever you decide to change the event args you have to change only your custom args class and some of the event invocation points. "Some", because you can provide suitable default values for fields of your args class and only provide the values when you need it.
Most of good/bad practice talks are focused on one thing - ease of change. Changes always happen. The fewer things you have to alter when they do, the better. Design has a lot of iterations and the odds are that you'll have to change your interfaces and types a lot. You don't want to change tens of signatures whenever you decide that you don't need the parameter anymore. So use the EventArgs or EventArgs<T> and save yourself some headache.
Another minor point is that you'll probably want to declare some methods that take care of raising the events, like this:
public static public void FireEvent<T>(this EventHandler handler, object sender, EventArgs<T> e)
where T : EventArgs
{
if (handler != null)
handler(sender, e);
}
If all your events are EventHandler-based, you can use one all-purpose method for things like that. If not, then you'll eventually have tens of them.

Related

Event vs EventHandler

I read a lot of materials but I still did not understand, can someone explain me, please?
What do I know:
Declare an event:
public event MyEvent myEvent;
Vs.
declare EventHandler:
public EventHandler MyEventHandler;
While EventHandler is a keyword in .NET:
public delegate void EventHandler (Send object, EventArgs e);
So, EventHandler is a delegate and not an event, since it is not used in the keyword 'event'?
And when should I use 'Event' and when 'EventHandler'?
Ah, the event vs. delegate question. I remember having that question as well...
So, suppose you're making a class called "Cat" and you want to let people know when your cat is hungry. You could do that in one of two ways: by exposing a delegate on the Cat class or by exposing an event.
You can think of a delegate as a pointer to a function (or method). So let's say there's a class called "Person" that has a method called FeedCat(Cat cat).
The delegate way
If your cat is exposing a delegate called HungryDelegate, the person can point the delegate to their FeedCat method, so that when the cat is hungry, it has a way to call the FeedCat method on the person.
The problem here is, only one person can feed the cat. Suppose you want multiple people to be able to feed the cat. People can be busy doing other things, so it's important for the cat to be able to tell multiple people that it's hungry. That way, people get notified of this, and they can check up on the cat when they have a chance, see if someone already fed the cat, and feed it if not.
Events to the rescue:
An event is basically a list of delegates (of a certain type). If you expose a Hungry event on your "Cat" class, multiple people can ADD pointers (delegates) to their FeedCat methods.
They can also remove the pointers (delegates) to their FeedCat methods if they like. Let's say a person moved out, and their sister is now taking care of the cat. The person can remove their delegate for their own FeedCat function from the cat, so that they no longer get notified that the darn cat is hungry.
Events vs. multicast delegates?
Technically, in order to be able to provide multiple delegates, you could use the so called MultiCastDelegates instead of events. They are composite delegates (a linked list or delegates). The problem there is, everyone can mess with them from the outside. An evil person could remove everyone else's "FeedCat" delegates, and the poor cat would starve (or would have to learn to hunt).
The important thing when using events is that the person cannot see other people's delegates that are added to the event, and it can't remove them or interact with them (in principle).
Exposing a (public) field
public EventHandler MyEventHandler;
is a bad practice: one can easily ruin the code with a small typo:
MyClass demo = new MyClass();
demo.MyEventHandler += MyMethod;
...
// Can you see the error? = instead of correct += ?
// MyMethod will not be called since this assignment
demo.MyEventHandler = MyReaction;
That's why you should use event which has been specially desinged for this
public event EventHandler MyEventHandler;
if we try the previous demo we'll get compile time error:
MyClass demo = new MyClass();
...
demo.MyEventHandler = MyReaction; // <- doesn't compile, insist on +=
In very rare cases you may want explicit delegate-based fields, but these field should be concealed, say, be private, not public:
// We don't expose the field
private EventHandler m_MyEventHandler;
// ... But event:
public event EventHandler MyEventHandler {
add {
//TODO: extra logic on += operation
m_MyEventHandler += value;
}
remove {
//TODO: extra logic on -= operation
m_MyEventHandler -= value;
}
}
DELEGATES, EVENTS(EVENT HANDLERS/EVENT LISTENERS), CONCEPTS(MULTICASTING/BROADCASTING), ACTION & FUNC
This will be a long one but its the simplest explanation, the problem this is such a nuisance of a topic is because people are just using different words to explain the same thing
First of all, you should know a few things
DELEGATES: It's nothing but a list of methods, why create a list? because when your code is being executed, that list is taken and every method there is executed one by one, just don't listen to textbook definitions take this and you will be all right
also called :
a pointer to a function
a wrapper for a method that can send and receive methods just like a variable
to create a delegate you go
[[access modifier] delegate [return type] [delegate name]([parameters])]
example: public delegate int demo(int a);
now to execute all these methods stored in a list called delegate, you go
1. demo.invoke(a);
2. demo(a); ..... both are valid
using the dot and explicitly saying invoke shines in async programming where you use beginInvoke, but that is out of the scope of this topic
there is one more thing called "Creating an object of the delegate/instantiate Delegate" which is pretty much as it sounds but just to avoid confusion it goes like (for the above example )
example : demo del = new demo(); (or) Public demo del = null;
to add any method to the list called delegate you go += and you also need to remove it once the "requirements of the methods are met" you go -=
(requirements of the methods are met mean you no longer need the method to be active or aka "listening") if you don't remove it, it could cause a "memory leak" meaning your computers ram will be eaten alive, technically allocated memory will not be released
example: say there is a method
public int calculate (int c)
to add this method to delegate you go
1. del = calculate;
2. del += calculate; .... all are valid
to remove
del -= calculate
first of all notice the similarities between the delegate and the method, the return type(output) and the input/parameters are the same, and that is a rule you just cannot add any random or a bunch of methods in a delegate it needs to follow the input-output rule
now why are there 2 different ways to do one thing, the only thing different is the assignment operators (+, =), this introduces a new topic called
EVENTS
which is nothing but a constrained version of a Delegate, It's still a List of methods don't confuse when people explain these terminologies, they change the name, so stick with this to understand
what is the constraint? you cannot do this del = calculate;
what's the harm in it, say a bunch of methods are added to the Delegate(List), you do that 👆 all are wiped out and only a single method "calculate" remains, so to prevent that Events are used,
Event Syntax
Public Event demo del = null;
One more thing you cannot do with events is invoke the delegate directly like demo.invoke since its public it can be accessed and invoked but with events, it can't
now you just add the methods to the event (a special type of delegate)
when to use an event vs a delegate, depends on your situation but pragmatically events are popular
few more keywords
MULTICASTING: nothing but adding more than one method to a delegate
BROADCASTING: adding more than one method to an event
PUBLISHER: the one that executes the method (term used in broadcasting), only a single entity
SUBSCRIBER: The methods that are being executed, can be multiple
LISTENER: the same thing as a subscriber but the term is used in multicasting
EVENT HANDLER: same thing as a subscriber/event listener so what the difference? it's basically the same thing, some say an eventlistener detect for the event to occur and the event handler "handles" or execute the code, ITS THE SAME THING PRACTICALLY!
action and func are just delegates that have been created and instantiated so 2 lines of code in a word, the difference is just in return types
ACTION: does not return anything while taking 0 or more than 1 input
FUNC: returns one thing and takes in parameters
if you don't do good with reading here is the best video on this topic
https://www.youtube.com/playlist?list=PLFt_AvWsXl0dliMtpZC8Qd_ru26785Ih_

Events aren't fields - I don't get it

In C# in depth (an excellent book thus far), Skeet explains events aren't fields. I read this section many times and I don't understand why the distinction makes any difference.
I am one of those developers that confuse events and delegate instances. In my mind, they are the same. Aren't both just a form of indirection? We can multicast both. An event is setup as a field as shorthand...sure. But, we are adding or removing handlers. Stacking them up to be called when the event fires. Don't we do the same thing with delegates, stack them up and call invoke?
The other answers are basically correct, but here's another way to look at it:
I am one of those developers that confuse events and delegate instances. In my mind, they are the same.
An old saying about not seeing the forest for the trees comes to mind. The distinction that I make is that events are at a higher "semantic level" than a field of delegate instance. An event tells the consumer of the type "hi there, I am a type that likes to tell you when something happens". The type sources an event; that's part of its public contract.
How, as an implementation detail, that class chooses to keep track of who is interested in listening to that event, and what and when to tell the subscribers that the event is happening, is the business of the class. It happens to typically do so with a multicast delegate, but that's an implementation detail. It is such a common implementation detail that it is reasonable to confuse the two, but we really do have two different things: a public surface, and a private implementation detail.
Similarly, properties describe the semantics of an object: a customer has a name, so a Customer class has a Name property. You might say that "their name" is a property of a customer, but you would never say that "their name" is a field of a customer; that's an implementation detail of a particular class, not a fact about the business semantics. That a property is typically implemented as a field is a private detail of the class mechanics.
Properties aren't fields either, although they feel like them. They are actually a pair of methods (getter and setter) with special syntax.
Events are similarly a pair of methods (subscribe and unsubscribe) with special syntax.
In both cases, you usually have a private "backing field" inside your class, which holds the value manipulated by the getter/setter/subscribe/unsubscribe methods. And there's an auto-implemented syntax for both properties and events where the compiler generates the backing field and accessor methods for you.
The purpose is also the same: Properties provide restricted access to a field, where some validation logic is run before storing a new value. And an event provides restricted access to a delegate field, where consumers can only subscribe or unsubscribe, not read the list of subscribers, nor replace the whole list at once.
Let's consider the two ways to declare events.
Either you declare an event using an explicit add/remove method, or you declare an event without such methods.
In other words, you declare the event like this:
public event EventHandlerType EventName
{
add
{
// some code here
}
remove
{
// some code here
}
}
or you declare it like this:
public event EventHandlerType EventName;
The thing is, in some ways they're the same thing, and in other ways, they're completely different.
From the perspective of outside code, that is ... code outside of the class publishing the event, they're the exact same thing. To subscribe to an event, you call a method. To unsubscribe, you call a different method.
The difference is that in the second example code above, those methods will be provided by the compiler for you, however, that's still how it's going to be. To subscribe to the event, you call a method.
The syntax to do so, in C#, however, is the same, you do either:
objectInstance.EventName += ...;
or:
objectInstance.EventName -= ...;
So from the "outside perspective", the two ways are no different at all.
However, inside the class, there is a difference.
If you try to access the EventNameidentifier inside the class, you're actually referring to the field that backs the property, but only if you use the syntax that doesn't explicitly declare an add/remove method.
A typical pattern is like this:
public event EventHandlerType EventName;
protected void OnEventName()
{
var evt = EventName;
if (evt != null)
evt(this, EventArgs.Empty);
}
In this case, when you're referring to EventName, you're actually referring to the field that holds the delegate of type EventHandlerType.
However, if you've explicitly declared the add/remove methods, referring to the EventName identifier inside the class will be just like outside of the class, since the compiler cannot guarantee that it knows the field, or any other mechanism, in which you store the subscription.
An event is an accessor for a delegate. Just like a property is an accessor for a field. With the exact same utility, it prevents code from messing with the delegate object. Like a property has a get and set accessor, an event has the add and remove accessor.
It does behave somewhat different from a property, if you don't write the add and remove accessors yourself then the compiler auto-generates them. Including a private backing field that stores the delegate object. Similar to an automatic property.
You don't do this often but it is certainly not unusual. The .NET framework pretty commonly does so, for example the events of the Winforms controls are stored in an EventHandlerList and the add/remove accessors manipulate that list through its AddHandler() and RemoveHandler() methods. With the advantage that all the events (there are many) require only a single field in the class.
I can add to the former answers that delegates can be declared inside a namespace scope (outside a class) and events can be declared only inside a class.
This is because delegate is a class!
Another distinction is that , for events, the containing class is the only one that can fire it.
You can subscribe/unsubscribe to it via the containing class, but can't fire it (in contrast to delegates).
So maybe you can understand now why the convention is to wrap it inside a protected virtual OnSomething(object sender, EventArgs e). It is for the descendants to be able to override the implementation of the firing.

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?
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).
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
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.
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.)
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.
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:
jgauffin
Baszz
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:
So that classes that inherit from your class can still raise the event, with a derived EventArgs class.
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:
More info about how to design events: http://msdn.microsoft.com/en-us/library/ms229011.aspx
A detailed how-to on using this pattern: http://msdn.microsoft.com/en-us/library/w369ty8x.aspx
In order to follow the .net standards, the recommanded way to create an event is:
Create a class that inherits from EventArgs
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;
}
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.

Should EventHandler always be used for events?

I've been merrily writing events using custom delegate types and the generic Action delegate type, without really thinking about what I was doing.
I have some nice extension helpers for Action and EventHandler which makes me tend to use those predefined delegate types rather than my own, but aside from that...
Is there a good reason other than convention to favour EventHandler and EventHandler<T> over custom delegate types or generic Action delegate types?
The main advantage of the signature EventHandler<T> over using one parameter for each member of your EventArgs is that you can add additional properties to your EventArgs without breaking compatibility.
IMO this is the most important argument. Being able to extent your EventArgs without breaking subscribing code is very nice. But of course you can achieve the same with any signature that uses some kind of property-bag parameter instead of a parameter per property.
Then there is variance, EventHandler<Base> is convertible to EventHander<Derived>, so you can write an EventHandler with parameter EventArgs and it can subscribe to events which have more specific EventArgs.
Extension methods are another plus, but you already mentioned that.
No, no good reason.
If your events do not require EventArgs or a sender object, then you don't need to use EventHandler or EventHandler<T>.
The reason EventHandler is convention is that it supports a unified way of working with events.
The EventHandler for example always has a sender which comes out handy.
Rule of thumb here is that when you are working with WinForms and creating custom controls or extending e.g. Form, you really should use EventHandler. For your own classes: go wild.

Benefits of events using EventArgs/EventArgs<T> delegate types instead of…

I understand the benefits of events using delegate types with signature delegate void delegate_name(object sender, EventArgs e)
a) But besides the fact that it may save us some typing, are there any other reasons why we should use already defined delegate types EventHandler/EventHandler<T> instead of declaring our own delegate types with signature delegate void delegate_name(object sender, EventArgs e)?
b) Two other reason I can think of for using the predefined delegate types EventArgs/EventArgs<T> are:
people consuming particular event ( say event EventHandler my_event ) will immediately know how to use that event?
perhaps some popular third party methods accept as parameters EventHandler/ EventHandler<T> delegate types, and thus if there’s any chance that our code may use those third party methods, we should use predefined delegates EventHandler/Eventhandler<T>?
thank you
To me, the question is a little strange. What would be the benefit of doing this otherwise (defining delegate types that exactly match EventHandler<TEventArgs> for some TEventArgs)?
That said, there is at least one benefit I can think of to doing it the "normal" way: certain APIs already expect to deal with EventHandler<TEventArgs> delegates; for example, Rx Extensions includes a method that looks like this:
Observable.FromEvent<TEventArgs>(
Action<EventHandler<TEventArgs>> addHandler,
Action<EventHandler<TEventArgs>> removeHandler
);
If you defined your own delegate, using methods like this -- which expect EventHandler<TEventArgs> delegates -- would become more complicated than necessary for no added benefit (that I can see, anyway).
You forgot an important one:
the lunatic that will maintain your code some day will find out where you live and hurt you.
You've answered your own question:
Syntactical Sugar (less to write) to maintain the convention
Interoperability (using the EventHandler type let's you easily integrate events from other libraries
In short; there's no good reason not to use it unless you're forced to (which typically is the result of people not being aware of it, or not understanding it).
From Pro C# 2008 and the .NET 3.5 Platform:
When the compiler processes the event keyword, you are automatically provided
with registration and unregistration methods* as well as any necessary member
variables** for your delegate types. ...To be sure, the event keyword is
little more than syntactic sugar in that it simply saves you some typing time.
* This includes overloading the handy += and -= operators.
** ...which are already marked private so they can't end-run.
When you use the generic EventHandler delegate, you don't even have to write out your custom delegate type at all.
I'm going to rock the boat here and propose something entirely heretical. I used to be firmly in the EventArgs camp because I clung to the "MS recommends this and it's always been done this way" mentality, but over time I came to hate EventArgs. Why?
It promotes a .NET-1.0ish style of coding which relies upon weak-typing/type casting and makes me feel unclean.
It forces your class which implements the event to pollute the heap with new EventArg instances every time it fires, which also makes me uneasy. Why not have my events give subscribers exactly what they need instead of wrapping it up in an extra class that does nothing for me.
The signatures of your callback methods which subscribe to the event look like garbage and have very little semantic detail - e.g. object sender - WHAT is sender?!?!
What I do now is declare my own event handler delegates, which I store neatly in their own "Delegates" folder in my solution as well as their own namespace. So my delegate may reside in its own file like this:
namespace MyAPI.Data.Delegates
{
public delegate void DataEventHandler<TData>(DataFeed<TData> sender, TData data);
}
The event declaration now looks like this:
public event DataEventHandler<TData> DataReady = delegate { };
Benefits to this approach:
Method signatures have more semantic detail. You know WHO is sending WHAT.
Strong-typing is preserved. No more casting object sender to what you think it should be.
You don't have to new() up objects and pollute the heap, which can be problematic if your event fires frequently. Just pass your subscribers exactly what they need, whether it be an object reference or a value type.
By using the ___EventHandler naming convention for your delegates, you are still promoting a uniform style for your code which makes it easy for users of your API to know what your intent is.
The only "drawback" is that it makes it hard for users of your code to wire up your event to existing methods which have the object sender, EventArgs e signature. However, this point is moot because if your event delivers any extra data (e.g. you created your own EventArgs subclass) then they'll have to change the method signature anyway (or cast to your subclass type). Either way, that's still nasty.
That's why I like my way.

Categories

Resources