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

According to Microsoft's recommendation for event naming , the sender parameter in the C # event handler "always has an object type, even if you can use a more specific type."

This leads to a lot of event processing code, for example:

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

Why doesn't the convention recommend declaring an event handler with a more specific type?

 MyType { public event MyEventHander MyEvent; } ... delegate void MyEventHander(MyType sender, MyEventArgs e); 

Do I miss Gocha?

For posterity: I agree with the general opinion in the answers that the agreement is to use an object (and transmit data through EventArgs ), even when it is possible to use a more specific type, and in real-world programming it is important to comply with the convention.

Change: search bait: RSPEC-3906 rule "Event handlers must have the correct signature"

+70
c # events
Sep 17 '09 at 9:24
source share
12 answers

Well, this is more a template than a rule. This means that one component can send an event from another, preserving the original sender, even if it is not an ordinary type that raises an event.

I agree with this a little strange, but it is probably worth sticking to the convention just for the sake of acquaintance. (Knowledge for other developers, that is.) I have never been particularly interested in EventArgs myself (given that he himself does not transmit any information), but this is a different topic. (At least we now have EventHandler<TEventArgs> , although that would help if EventArgs<TContent> existed for the normal situation where you just need one value to propagate.)

EDIT: this makes the delegate a more general goal, of course, - one type of delegate can be reused for multiple events. I'm not sure that I buy this as a particularly good reason - especially in the light of generics - but I think this is something ...

+39
Sep 17 '09 at 9:29
source share

I think there is a good reason for this agreement.

Take (and expand) the @erikkallen example:

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

This is possible (and was from .Net 1 to generics) because covariance is supported.

Your question makes sense if you go from top to bottom - that is, you need an event in your code, so you add it to your control.

However, the agreement is to simplify the recording of components in the first place. You know that for any event the main template will work (object sender, EventArgs e).

When you add an event, you do not know how it will be used, and you do not want to arbitrarily restrict developers using your component.

Your example of a generic, strongly typed event makes sense in your code, but will not match other components written by other developers. For example, if they want to use your component with the above:

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

In this example, an additional type constraint simply creates pain for the remote developer. Now they need to create a new delegate just for your component. If they use the load of your components, they may need a delegate for each of them.

I believe that the agreement deserves attention for anything external or that you expect to use outside the close command.

I like the idea of ​​common args events - I'm already using something similar.

+17
Sep 17 '09 at 12:31
source share

I use the following delegate if I 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 as follows:

 public event EventHandler<TypeOfSender, TypeOfEventArguments> CustomEvent; 
+10
Sep 17 '09 at 12:15
source share

Generics and history will play a big role, especially with the number of controls (etc.) that expose similar events. Without generics, you will have many events subject to Control , which is pretty much useless:

  • you still need to do something useful (except maybe a check that you can do with object )
  • you cannot reuse events for non-management

If we look at generics, then again everything will be fine, but then you will start to get into problems with inheritance; if class B : A , then should events on A be EventHandler<A, ...> , and events on B be EventHandler<B, ...> ? Again, a very confusing, complex tool and a bit messy in terms of language.

As long as there is a better option that covers all of these, object works; events are almost always on instances of the class, so there is no boxing, etc. - just the cast. And casting is not very slow.

+5
Sep 17 '09 at 9:48
source share

I assume that since you should be able to do something like

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

Why are you doing safe listing in your code? If you know that you use this function only as an event handler for the repeater, you know that the argument is always of the correct type, and you can use metalization instead, for example. (Repeater) instead of (sender as a relay).

+4
Sep 17 '09 at 9:54
source share

For no good reason, now there is a coincidence and a contradiction, I think it is good to use a strongly typed Sender. See the discussion in this question.

+3
Sep 17 '09 at 12:09
source share

Agreements exist only to impose consistency.

YOU CAN get a lot of event handlers if you want, but ask yourself if this will make any technical advantages?

You should keep in mind that event handlers do not always have to send a sender ... most of the event handling code that I saw in real practice does not use the sender parameter. This is there if necessary, but quite often it is not.

I often see cases where different events on different objects will share one common event handler, which works because this event handler is not related to who was the sender.

If these delegates were strongly typed, even with the clever use of generics, it would be very difficult to share an event handler. In fact, strictly typing it, you impose the assumption that the handlers should take care of what the sender is when it is not a practical reality.

I assume that you should be asking why you are decisively recruiting event handling delegates? By doing this, do you add any significant functional benefits? Do you make use more consistent? Or are you just imposing assumptions and restrictions just for strong input?

+1
Sep 17 '09 at 10:20
source share

You speak:

This leads to a lot of code event handling: -

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

Is there really a lot of code?

I would advise never using the sender parameter for an event handler. As you noticed, this is not statically printed. This is not necessarily the direct sender of the event, because sometimes the event is redirected. Thus, the same event handler may not even receive the same type of sender object each time it starts. This is an unnecessary form of implicit coupling.

When you end an event, at that moment you should know what object the event is on, and that is what interests you the most:

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

And everything related to the event should be in the second parameter of EventArgs.

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

I asked a similar question here.

+1
Sep 17 '09 at 10:21
source share

This is because you can never be sure who fired this event. It is not possible to limit what types are allowed to trigger a specific event.

+1
Sep 17 '09 at 10:53
source share

The use of the EventHandler (object sender, EventArgs e) scheme is designed to provide all events with a means of identifying the source (sender) and providing a container for all the event-specific payload. This template also has the advantage of being able to generate several different events using the same delegate type.

As for the default arguments of this delegate ... The advantage of having a single bag for the whole state that you want to pass along with the event is pretty obvious, especially if there are a lot of elements in this state. Using an object instead of a strong type allows you to pass the event together, possibly to assemblies that do not have a reference to your type (in this case, you can argue that they will not be able to use the sender in any case, but this is another story - they can still get event).

In my experience, I agree with Stephen Redd, very often the sender is not used. The only cases I need to identify the sender is handling user interface handlers, and many controls use the same event handler (to avoid code duplication). However, I deviate from his position, because I see no problems with defining strongly typed delegates and generating events with strongly typed signatures, in the case when I know that the handler will never be interested in who the sender is (indeed, often it should not have which either volume in this type), and I don’t want the inconvenience of the filling state in the bag (a subclass of the EventArg class or general) and its unpacking. If I have only 1 or 2 elements in my state, I am fine creating this signature. This is a matter of convenience for me: strong typing means that the compiler keeps me on my toes and reduces the appearance of the branch, for example

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

which makes the code look better :)

Saying this, this is just my opinion. I often deviated from the recommended event pattern, and I did not suffer from this. It is important to always clearly understand why it is normal to deviate from it. Good question! .

+1
Sep 17 '09 at 11:08
source share

Ok, that’s a good question. I think because any other type could use your delegate to declare the event, so you cannot be sure that the sender type is really "MyType".

0
Sep 17 '09 at 9:29
source share

I prefer to use a specific type of delegate for each event (or a small group of similar events). The useless sender and eventargs simply clutter up the api and distract from the actually corresponding bits of information. The ability to "forward" events to classes is not something that I have not yet found useful - and if you redirect events like this to an event handler, which is a different type of event, and then force the event to end on its own and provide the appropriate parameters - it is a little effort. In addition, the forwarder tends to better understand how to "transform" the parameters of the event than the final receiver.

In short, if there is no obsessive argument, dump useless, confusing parameters.

0
Sep 17 '09 at 12:27
source share



All Articles