开发者

Why does MSDN recommend including object sender in delegate declarations?

I was reading this page and I noticed how it said this is standard guidelines:

The .NET Framework guidelines indicate that the delegate type used for an event should take two parameters, an "object source" parameter indicating the source of the event, and an "e" parameter that encapsulates any additional information about the event.

I can understand how having an object sender could be useful in some circumstances, but I could see the exact opposite in others. For example,

  1. What if a class handling the event should not have any knowledge about who fired it? Coupling, cohesion, and all of that.

  2. In my case, I already have a reference to the object as a member variable. That is how I subscribe to the event. There will only ever be one instance of it so there's no reason to cast the sender object rather than just using the member variable.

  3. In my program the sender object should not be known at all to the clients. It's hard to explain what I am doing but basically I have a class with an internal constructor within a library that is used by two other classes also within that library. My client classes are subscribing to events from those two classes but the events are originally invoked from this internal class that clients should not have any knowledge of.

  4. It is confusing to clients of the event handler. Libraries should be simple to understand and in my case, there is no reason to ever use the sender variable. None.开发者_Python百科 Then why include it?

That being said, why does Microsoft indicate that event handlers should follow these guidelines? Isn't it not always the best choice?

EDIT: Thanks for the replies everyone. I've decided to go with the majority and use EventHandler<T> for all my events in this library.


You are fighting against the wind, the .NET framework has certain design, rules and guidelines and when using it, if you want to use it correctly, you are supposed to follow those directions.

if you use raw delegates you have all the freedom you want but as stated above if you are declaring a delegate type for an event you should include sender object and EventArgs object as well (base or derived class).

if you break those rules, as I said a moment ago in my answer to your other question: Should I use EventArgs or a simple data type?, you could potentially end up in a situation where your code breaks.

Simplyfying at the maximum, when the framework invokes an OnClick event on a control, the .NET Framework does pass the sender and an EventArgs instance... if the event would not comply, something could break.

if you want full freedom then use simple delegates but not events.


First of all, it's important to note that a guideline is not a law.

All hell (or the programmer equivalent) will not break lose if you don't follow the guidelines.

As such, feel free to change the signature of your events appropriately.

However, it is just as important to know why these guidelines were added to begin with, and one big part of the answer(s) to that question is versioning.

By having the following two parts, and only those two parts:

  1. The source of the event, typed as "generic" as possible (note, event signatures were designed long before proper generics were introduced into the system, so object is as generic as can be from back then)
  2. An object inheriting from EventArgs

then you are designing code that is more resilient to changes.

First of all, since you're not "allowed" to add or remove parameters, all future versions of your event will still have only sender and e.

Secondly, there's a second part to the guideline regarding the e parameter. If you in a new version of your class library decides to change the signature of an event handler by changing the type of the e parameter, you're supposed to make it more specific by descending from your current type, and passing the descendant instead.

The reason for this is that existing code that already handles your current (old) type will still work.

So the entire reasoning behind the guideline is to:

  1. Stay consistent (as others have mentioned)
  2. Design for the future (by making sure code written against version X of your class still works when you release version X+1, without manual changes to that code)

Now, if any of this is not a concern for your case, feel free to not follow the guideline.

In fact, you can make an event out of an Action and it'll work just fine.


Why? People always ask this. In this end, this is just about a pattern. By having event arguments packaged in a class you get better versioning semantics. By having a common pattern (sender, e) it is easily learned as the signature for all events. I think back to how bad it was with Win32—when data was in WPARAM versus LPARAM, and so on. The pattern becomes noise and developers just assume that event handlers have scope to the sender and arguments of the event.

-Chris Anderson, in Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries

If you're a .NET developer and you haven't read that book, you're missing out. It gives you a window ;) into the minds of the Microsoft .NET Framework designers, and a lot of best practices (including the reasoning behind them).

(Plus, you can run FxCop to verify that these practices are being followed.)


I think the reason for the pattern is to enforce some consistency. The sender parameter allows re-use of a single handler for multiple publishers (buttons, tables).

To address your points:

1) simply don't use it. That is common and doesn't really hurt any good practice.

2) that's OK, again ignore the sender

3) is in total contradiction of what you said under 2) ...
And for the rest it is the same as 1). You could even consider passing null as sender.

4) "then why include it" - there are other use cases that do require the sender.


But do note this is just a guideline for libraries confirming to the BCL.
Your case sounds more like a specific application (not a library) so feel free to use any parameter scheme you like. The compiler won't complain.


Guidelines such as this allow for predictability on the part of the consumer of the event. It also allows for handling of additional scenarios you may never have considered when you created the event, especially if your library is used by third party developers.

It allows the method handling the event to always have the correct object instance at hand as well as basic information regarding why the event was fired.


It's just good practice, but you'll be fine as long as you don't need to know about the object that fired the event or any other info related to the object. I for one always include it since you never know when you'll need it.

My suggestion is to stick with it, it does not hurt at all.


There would have been nothing wrong, semantically, with having event handlers that are interested in where events came from use a derivative of EventArgs which included such a field. Indeed, there are many situations where that would be cleaner than passing Sender as a separate parameter (e.g. if a keyboard-shortcut processor needs to fire a Click handler for a button, the event shouldn't really be considered to have been raised by the button, but rather raised on the button's behalf). Unfortunately, incorporating that information within an EventArgs-derived type would make it necessary to create a new instance of an EventArgs-derived type every time an event is raised, even if it could otherwise use EventArgs.Empty. Using a separate parameter to pass the information eliminates the need to have every event create a new object instance in that common case.

Although it might have been possible to have handlers that care about where an event came from use a parameter for that, and have those that don't care about it omit the parameter, that would have required that any helper methods and classes which assist with handling event subscriptions would need to have versions for events which did or did not include the parameter. Having all events take two parameters, one of which is of type Object and one of which is of a type derived from EventArgs makes it possible for one helper method or class to be capable of handling all events.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜