Создание пользовательских событий - Object Sender или Typed Sender? - PullRequest
8 голосов
/ 10 июля 2010

Я искал в архивах и нашел много вопросов о том, что такое отправитель и почему вы должны использовать шаблон, но я ничего не видел о пользовательском событии и типе если отправитель.

Допустим, я создаю пользовательский класс с именем Subscription, и он реализует ISubscription, и у меня есть несколько аргументов события, называемых SubscriptionEventArgs. Если в подписке было событие с именем Изменено, что не так в сигнатуре события Изменено (отправитель ISubscription, SubscriptionEventArgs e)?

Небольшой код, помогающий задать вопрос:

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);
        }
    }
}

Если вы просто презираете использование действия вместо «EventHandler», то вы можете сделать то же самое, но с пользовательским универсальным «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);
        }
    }
}

В ответ на запрос Ганса об образце обработчика событий:

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

Поиск подписки в списке может показаться тривиальным, но что, если я работаю с очень большими наборами данных и новые объемы данных поступают в приложение через поток в реальном времени. Затраты на то, чтобы остановиться и получить ссылку из списка или пройти этапы кастинга, не имеют смысла. Они дали нам дженерики в 2.0 для решения этой проблемы, поэтому я не понимаю, почему у нас тоже не было универсального обработчика событий, и это привело меня к вопросу, что не так с универсальным обработчиком событий?

1 Ответ

0 голосов
/ 11 июля 2010

Я на самом деле весьма озадачен, почему при разработке .Net Framework v2 MS не предоставила EventHandler так, как вы описали - с TSender и TEventArgs в качестве обоих общих аргументов.(В v1 и v1.1, поскольку у них не было обобщений, я полностью понимаю, почему они не создали тысячи дополнительных типов делегатов для обработки всех возможных событий.) Если я правильно помню, вы все равно можете использовать обобщенный обработчикчтобы прослушать более конкретное событие:

public event EventHandler<Button, MouseDownEventArgs> MouseDown;

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

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

Поскольку вы не подвергаете наблюдателя наблюдаемому, я не понимаю, как это может быть проблемой;вы просто исключаете необходимость выполнять проверку типов «на всякий случай», как только попадаете в обработчик событий.Я думаю, что это была бы фантастическая практика, хотя и немного нестандартная, поскольку MS решила не включать ее.

Как отмечалось в моем комментарии выше, я бы предпочел увидеть следующее определение EventHandler, так чтовы действительно всегда можете использовать очень обобщенный метод обработчика, как мой пример кода:

public delegate void EventHandler<TSender, TEventArgs>(TSender sender, TEventArgs e)
    where TEventArgs : EventArgs;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...