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

后端 未结 12 774
半阙折子戏
半阙折子戏 2020-12-02 08:19

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

相关标签:
12条回答
  • 2020-12-02 09:00

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

    0 讨论(0)
  • 2020-12-02 09:01

    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;
    
    0 讨论(0)
  • 2020-12-02 09:01

    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?

    0 讨论(0)
  • 2020-12-02 09:02

    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.

    0 讨论(0)
  • 2020-12-02 09:06

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

    0 讨论(0)
  • 2020-12-02 09:09

    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.

    0 讨论(0)
提交回复
热议问题