views:

147

answers:

1

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?

A: 

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;
mattdekrey
Probably because most of those events are declared in a base class (Control - ButtonBase - ...) and therefor wouldn't have the Button class as sender. (and no covariance then yet ;)
Stormenet
That's fair, but even using the most restrictive object, even if it was an abstract base class or even an interface that it knew about would be helpful. For instance, having quick access without casting for a CaptureMouse call from a MouseDown event. And, since the explicit delegate wrapping works (as above, without generics), we'd still be able to use a generalized event handler.
mattdekrey