Creating custom events - Object Sender or Typed Se

2019-04-06 10:57发布

问题:

I searched through the archives and I found lots of questions about what sender is and why you should use the pattern but I didn't see anything about a custom event and the type if sender.

Say I am creating a custom class called Subscription and it implements ISubscription and I have some event args called SubscriptionEventArgs. If Subscription had an event called Changed what is wrong about the event signature Changed(ISubscription sender, SubscriptionEventArgs e)?

A little code to help drive the question:

public class SubscriptionEventArgs : EventArgs
{
    // guts of event args go here
}

public interface ISubscription
{
    event Action<ISubscription, SubscriptionEventArgs> Changed;
}

public class Subscription : ISubscription
{
    public event Action<ISubscription, SubscriptionEventArgs> Changed;

    private void OnChanged(SubscriptionEventArgs e)
    {
        if (Changed!= null)
        {
            Changed(this, e);
        }
    }
}

If you just despise the use of action in place of "EventHandler" then you could do the same thing but with a custom generic "EventHandler".

public delegate void EventHandler<TSender, TEventArgs>(TSender sender, TEventArgs e);

public class SubscriptionEventArgs : EventArgs
{
    // guts of event args go here
}

public interface ISubscription
{
    event EventHandler<ISubscription, SubscriptionEventArgs> Changed;
}

public class Subscription : ISubscription
{
    public event EventHandler<ISubscription, SubscriptionEventArgs> Changed;

    private void OnChanged(SubscriptionEventArgs e)
    {
        if (Changed!= null)
        {
            Changed(this, e);
        }
    }
}

In response to Hans' request for a sample event handler:

public class SubscriptionCollection
{
    // what is actually holding the subscriptions is not really relevant to the question
    private List<ISubscription> _subscriptions;

    public SubscriptionCollection()
    {
        _subscriptions = new List<ISubscription>();
    }

    public void Add(ISubscription subscription)
    {
        subscription.Changed += new EventHandler<ISubscription, SubscriptionEventArgs>(Subscription_Changed);
        _subscriptions.Add(subscription);
    }

    private void Subscription_Changed(ISubscription sender, SubscriptionEventArgs e)
    {
        // Now when the subscription changed event is being handled by the collection
        // I don't have to look up the subscription in the list by some key and I don't 
        // have to cast sender to the correct type because the event handler was typed
        // correctly from the beginning.
    }
}

The lookup of the subscription in the list might seem trivial but what if I am working with very large sets of data and new volumes of data are coming at the application through a real-time stream. The cost of having to stop and get a reference out of a list or go through the steps of casting don't make sense. They gave us generics in 2.0 to solve that issue so I don't understand why we didn't get a generic event handler too and this led me to question what is wrong with a generic event handler?

回答1:

I actually am quite confused why, when designing the .Net Framework v2, MS didn't provide an EventHandler just the way you described - with the TSender and TEventArgs as both generic arguments. (In v1 and v1.1, since they didn't have generics, I completely understand why they didn't make thousands of extra delegate types to handle all the possible events.) If I remember properly, you can still use a generalized handler to listen to a more specific event:

public event EventHandler<Button, MouseDownEventArgs> MouseDown;

private void ObservingMethod(object sender, EventArgs e) { }

MouseDown += new EventHandler<Button, MouseDownEventArgs>(ObservingMethod);

Since you're not exposing the observer to the observable, I don't see how this could be a problem; you're just preventing the need to do type-checking 'just in case' once you get to the event handler. I think it'd be a fantastic practice, although a bit non-standard since MS decided not to include it.

As noted in my comment above, I would prefer to see the following definition of EventHandler, so that you really could always use a very generalized handler method, as my code sample:

public delegate void EventHandler<TSender, TEventArgs>(TSender sender, TEventArgs e)
    where TEventArgs : EventArgs;