75

According to Microsoft event naming guidelines, the sender parameter in a C# event handler "is always of type object, even if it is possible to use a more specific type".

This leads to lots of event handling code like:

RepeaterItem item = sender as RepeaterItem;
if (item != null) { /* Do some stuff */ }

Why does the convention advise against declaring an event handler with a more specific type?

MyType
{
    public event MyEventHander MyEvent;
}

...

delegate void MyEventHander(MyType sender, MyEventArgs e);

Am I missing a gotcha?

For posterity: I agree with the general sentiment in the answers that the convention is to use object (and to pass data via the EventArgs) even when it is possible to use a more specific type, and in real-world programming it is important to follow the convention.

Edit: bait for search: RSPEC-3906 rule "Event Handlers should have the correct signature"

max630
  • 8,762
  • 3
  • 30
  • 55
Iain Galloway
  • 18,669
  • 6
  • 52
  • 73
  • Actually, *all types* in C# are objects... – Wim ten Brink Sep 17 '09 at 09:31
  • 2
    While you're obviously correct, my question is about why the convention is to *not* use a more specific type if possible. – Iain Galloway Sep 17 '09 at 09:36
  • Yes, a return value in the event handler looks smelly. What if there's more than one handler? – erikkallen Sep 17 '09 at 09:57
  • I have a detailed discussion of using a strong-typed 'sender' parameter. In short, there is no drawbacks to using a strong-typed, and it's 100% backward compatible for C# (all versions) and VB.NET as of VB 2008. See here: http://stackoverflow.com/questions/1046016/event-signature-in-net-using-a-strong-typed-sender. – Mike Rosenblum Sep 30 '09 at 14:42
  • 2017 updated link to [Microsoft event handler design guidelines](https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/event#custom-event-handler-design) (which superseded the [Microsoft event naming guidelines](https://msdn.microsoft.com/en-us/library/h0eyck3s%28VS.71%29.aspx) for .NET 1.1). – Fabio Iotti Mar 11 '18 at 11:06

12 Answers12

38

Well, it's a pattern rather than a rule. It does mean that one component can forward on an event from another, keeping the original sender even if it's not the normal type raising the event.

I agree it's a bit strange - but it's probably worth sticking to the convention just for familiarity's sake. (Familiarity for other developers, that is.) I've never been particularly keen on EventArgs myself (given that on its own it conveys no information) but that's another topic. (At least we've got EventHandler<TEventArgs> now - although it would help if there were also an EventArgs<TContent> for the common situation where you just need a single value to be propagated.)

EDIT: It does make the delegate more general purpose, of course - a single delegate type can be reused across multiple events. I'm not sure I buy that as a particularly good reason - particularly in the light of generics - but I guess it's something...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 13
    While I think you're almost certainly correct, I'm still not sure I see the advantage. The event handler itself still has to know what type it's being passed (because it has to cast to it). I can't understand why it would be a bad thing to strongly-type it - other than your point about familiarity with the existing pattern... – Iain Galloway Sep 17 '09 at 09:35
  • 8
    @Iain: Yeah, I don't see much advantage of the pattern being defined this way either. It may well just be a bad decision from many years ago, which it's now too late to sensibly change. – Jon Skeet Sep 17 '09 at 09:38
  • 2
    Well, it was implemented ever since .NET 1.0, so it might be a backward-compatibility code smell, if anything. – Jon Limjap Sep 17 '09 at 12:03
  • 1
    @Mr Skeet, what did you think of the discussion about co/contravariance on this pattern here? http://stackoverflow.com/questions/1046016/event-signature-in-net-using-a-strong-typed-sender – MarkJ Sep 18 '09 at 13:26
  • 13
    **The above answer is _very_ old! Times _are_ changing!** :) `"Generic delegates are especially useful in defining events based on the typical design pattern because the sender argument can be strongly typed and no longer has to be cast to and from Object."` from [MSDN](http://msdn.microsoft.com/en-us/library/sx2bwtw7.aspx) – AnorZaken Dec 17 '14 at 16:07
  • @AnorZaken: And how many events in the BCL are defined that way? – Jon Skeet Dec 17 '14 at 16:12
  • @JonSkeet: A teacher told me once: "I wish I had become a math teacher, math changes less, as it is I have to learn new programming techniques every year." - it made me chuckle, but it's also very true. I believe this is an easy and small change for the better (as opposed to the tons of effort that is not uncommonly associated with change). Me and whoever wrote the MSDN page for delegates embrace and encourage this change - you and whoever wrote the BCL seemingly does not. With small gains either way it's mostly opinion based, thus a publicly held debate is basically aimed at swaying minds :) – AnorZaken Jan 07 '15 at 18:23
  • 2
    @AnorZaken: I'm not sure why you think I don't like the change - I'm just saying it's not really implemented in the BCL much yet, and it's worth understanding the reason for the old pattern. – Jon Skeet Jan 07 '15 at 19:33
18

I think there's a good reason for this convention.

Let's take (and expand on) @erikkallen's example:

void SomethingChanged(object sender, EventArgs e) {
    EnableControls();
}
...
MyRadioButton.Click += SomethingChanged;
MyCheckbox.Click += SomethingChanged;
MyDropDown.SelectionChanged += SomethingChanged;
...

This is possible (and has been since .Net 1, before generics) because covariance is supported.

Your question makes total sense if you're going top-down - i.e. you need the event in your code, so you add it to your control.

However the convention is to make it easier when writing the components in the first place. You know that for any event the basic pattern (object sender, EventArgs e) will work.

When you add the event you don't know how it will be used, and you don't want to arbitrarily constrain the developers using your component.

Your example of a generic, strongly typed event makes good sense in your code, but won't fit with other components written by other developers. For instance if they want to use your component with those above:

//this won't work
GallowayClass.Changed += SomethingChanged;

In this example the additional type-constraint is just creating pain for the remote developer. They now have to create a new delegate just for your component. If they're using a load of your components they might need a delegate for each one.

I reckon the convention is worth following for anything external or that you expect to be used outside of a close nit team.

I like the idea of the generic event args - I already use something similar.

Keith
  • 150,284
  • 78
  • 298
  • 434
  • 1
    Very good point. Wish I could give you +5. By using object for the sender, the calling assembly doesn't even need to know about the type that it's attaching an event to - only that it derives from some type that it does know about (the relevant one usually being Control). It won't be able to use the sender paramter usefully (because it doesn't know what type to cast it to), but it can and will pick up any state information it needs out of the EventArgs. – Iain Galloway Sep 17 '09 at 12:37
  • Yeah - meanwhile if you do know what sender should be you can cast it back. I can't imagine a scenario where you would have an event on a value type, so you're not hurting performance for the cast. – Keith Sep 17 '09 at 12:45
  • @IainGalloway and Keith -- so... you'd want someone to attach the event handler in the question to a textbox or button (or anything except a `RepeaterItem`) and then... what? The code hits and tries the cast, it returns null, then nothing happens... if the handler isn't designed to handle a particular type of object as sender, then why would you WANT it to be able to attach? Obviously there are going to be events that are generic enough to completely justify using `object` with your logic, but... often not as well(?) I might be missing something, so feel free to illuminate what that might be. – Code Jockey Jun 04 '14 at 12:45
  • This answer is completely wrong. Just like an event with a more specific `EventArgs` type can still use a handler defined with an `EventArgs` parameter, likewise an event with a more specific sender type than `object` can still use a handler defined with an `object` parameter. Your example of what won't work will work just fine. – Mike Marynowski Aug 21 '19 at 07:53
  • @MikeMarynowskiI didn't give an _"example of what won't work"_, we aren't really talking about whether something will run or compile, we're just talking about practice and why a pattern is useful. Events are _covariant_: you can pass a more specific type (say `MyType`) to an `object sender` param, but if it has an explicit type (say `ApiType sender`) then you can't pass `MyType` in. There are reasons you might want that restriction, and it isn't bad practice, but the OP was asking why `object sender` was a common pattern used as default by Microsoft. – Keith Sep 03 '19 at 10:37
13

I use the following delegate when I would prefer a strongly-typed sender.

/// <summary>
/// Delegate used to handle events with a strongly-typed sender.
/// </summary>
/// <typeparam name="TSender">The type of the sender.</typeparam>
/// <typeparam name="TArgs">The type of the event arguments.</typeparam>
/// <param name="sender">The control where the event originated.</param>
/// <param name="e">Any event arguments.</param>
public delegate void EventHandler<TSender, TArgs>(TSender sender, TArgs e) where TArgs : EventArgs;

This can be used in the following manner:

public event EventHandler<TypeOfSender, TypeOfEventArguments> CustomEvent;
Chris Shouts
  • 5,377
  • 2
  • 29
  • 40
  • +1 I agree with this 100%. I have a detailed discussion of this approach here: http://stackoverflow.com/questions/1046016/event-signature-in-net-using-a-strong-typed-sender. – Mike Rosenblum Sep 30 '09 at 14:43
5

Generics and history would play a big part, especially with the number of controls (etc) that expose similar events. Without generics, you would end up with a lot of events exposing Control, which is largely useless:

  • you still have to cast to do anything useful (except maybe a reference check, which you can do just as well with object)
  • you can't re-use the events on non-controls

If we consider generics, then again all is well, but you then start getting into issues with inheritance; if class B : A, then should events on A be EventHandler<A, ...>, and events on B be EventHandler<B, ...>? Again, very confusing, hard for tooling, and a bit messy in terms of language.

Until there is a better option that covers all of these, object works; events are almost always on class instances, so there is no boxing etc - just a cast. And casting isn't very slow.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
4

I guess that's because you should be able to do something like

void SomethingChanged(object sender, EventArgs e) {
    EnableControls();
}
...
MyRadioButton.Click += SomethingChanged;
MyCheckbox.Click += SomethingChanged;
...

Why do you do the safe cast in your code? If you know that you only use the function as an event handler for the repeater, you know that the argument is always of the correct type and you can use a throwing cast instead, e.g. (Repeater)sender instead of (sender as Repeater).

erikkallen
  • 33,800
  • 13
  • 85
  • 120
  • Well, that's kind of my point. If you know that the argument is always of the correct type, why can't you just pass in that type? Obviously there *are* situations where you want to use a less specific type (like the one you just described). The cast just looks a bit messy to me. Maybe I'm just being too overzealous. – Iain Galloway Sep 17 '09 at 09:58
  • (+1) for the example, I've expanded on it in my own answer. – Keith Sep 17 '09 at 12:48
3

No good reason at all, now there's covarience and contravarience I think it's fine to use a strongly typed Sender. See discussion in this question

Community
  • 1
  • 1
MarkJ
  • 30,070
  • 5
  • 68
  • 111
1

Conventions exist only to impose consistency.

You CAN strongly type your event handlers if you wish, but ask yourself if doing so would provide any technical advantage?

You should consider that event handlers don't always need to cast the sender... most of the event handling code I've seen in actual practice don't make use of the sender parameter. It is there IF it is needed, but quite often it isn't.

I often see cases where different events on different objects will share a single common event handler, which works because that event handler isn't concerned with who the sender was.

If those delegates were strongly typed, even with clever use of generics, it would be VERY difficult to share an event handler like that. In fact, by strongly typing it you are imposing the assumption that the handlers should care what the sender is, when that isn't the practical reality.

I guess what you should be asking is why WOULD you strongly type the event handling delegates? By doing so would you be adding any significant functional advantages? Are you making the usage more "consistent"? Or are you just imposing assumptions and constraints just for the sake of strong-typing?

Stephen M. Redd
  • 5,378
  • 1
  • 24
  • 32
1

You say:

This leads to lots of event handling code like:-

RepeaterItem item = sender as RepeaterItem
if (RepeaterItem != null) { /* Do some stuff */ }

Is it really lots of code?

I'd advise never to use the sender parameter to an event handler. As you've noticed, it's not statically typed. It's not necessarily the direct sender of the event, because sometimes an event is forwarded. So the same event handler may not even get the same sender object type every time it is fired. It's an unnecessary form of implicit coupling.

When you enlist with an event, at that point you must know what object the event is on, and that is what you're most likely to be interested in:

someControl.Exploded += (s, e) => someControl.RepairWindows();

And anything else specific to the event ought to be in the EventArgs-derived second parameter.

Basically the sender parameter is a bit of historical noise, best avoided.

I asked a similar question here.

Community
  • 1
  • 1
Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
1

It's because you can never be sure who fired the event. There is no way to restrict which types are allowed to fire a certain event.

codymanix
  • 28,510
  • 21
  • 92
  • 151
1

The pattern of using EventHandler(object sender, EventArgs e) is meant to provide for all events the means of identifying the event source (sender), and providing a container for all the event's specific payload. The advantage of this pattern is also that it allows to generate a number of different events using the same type of delegate.

As for the arguments of this default delegate... The advantage of having a single bag for all the state you want to pass along with the event is fairly obvious, especially if there are many elements in that state. Using object instead of a strong type allows to pass the event along, possibly to assemblies that do not have a reference to your type (in which case you may argue that they won't be able to use the sender anyway, but that's another story - they can still get the event).

In my own experience, I agree with Stephen Redd, very often the sender is not used. The only cases I've needed to identify the sender is in the case of UI handlers, with many controls sharing the same event handler (to avoid duplicating code). I depart from his position, however, in that I see no problem defining strongly typed delegates, and generating events with strongly typed signatures, in the case where I know that the handler will never care who the sender is (indeed, often it should not have any scope into that type), and I do not want the inconvenience of stuffing state into a bag (EventArg subclass or generic) and unpacking it. If I only have 1 or 2 elements in my state, I'm OK generating that signature. It's a matter of convenience for me: strong typing means the compiler keeps me on my toes, and it reduces the kind of branching like

Foo foo = sender as Foo;
if (foo !=null) { ... }

which does make the code look better :)

This being said, it is just my opinion. I've deviated often from the recommended pattern for events, and I have not suffered any for it. It is important to always be clear about why it is OK to deviate from it. Good question! .

Sam Dahan
  • 871
  • 1
  • 8
  • 20
0

Well, that's a good question. I think because any other type could use your delegate to declare an event, so you can't be sure that the type of the sender is really "MyType".

Maximilian Mayerl
  • 11,253
  • 2
  • 33
  • 40
  • Everything I've read suggests this. However... Imagine you've got some event where the sender can be type A or type B. Then in the EventHandler you've got to try to cast to A then do A-specific logic, then try to cast to B and do B-specific logic. Alternately extract a shared interface I from A and B and cast to that. In the former case, what's the advantage over two seperate event handlers with A sender and B sender. In the latter case, what's the advantage over strongly-typing sender as I? – Iain Galloway Sep 17 '09 at 09:32
  • But they wouldn't be able to raise the event without an instance of your control (or use null for the sender, but that's uninteresting). – erikkallen Sep 17 '09 at 09:54
  • Yes, that'S right, they can't raise it. What I wrote above is just my opinion about why it is recommented to use object for the sender, I didn't say that I absolutely agree with it. ;) But on the other hand, i think one should follow the commonly used pattern, and therefore I always use object. – Maximilian Mayerl Sep 17 '09 at 10:00
  • *nods* I absoloutely agree. I have been and will be using object, I'm just wondering why :) – Iain Galloway Sep 17 '09 at 10:04
0

I tend to use a specific delegate type for each event (or a small group of similar events). The useless sender and eventargs simply clutter the api and distract from the actually relevant bits of information. Being able to "forward" events across classes isn't something I've yet to find useful - and if you're forwarding events like that, to an event handler that represents a different type of event, then being forced to wrap the event yourself and provide the appropriate parameters is little effort. Also, the forwarder tends to have a better idea of how to "convert" the event parameters than the final receiver.

In short, unless there's some pressing interop reason, dump the useless, confusing parameters.

Eamon Nerbonne
  • 47,023
  • 20
  • 101
  • 166