In a C# event handler, why must the "sender" parameter be an object?

Solution 1:

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...

Solution 2:

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.

Solution 3:

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;