EventHandler со строго типизированным отправителем в C # - PullRequest
6 голосов
/ 10 октября 2011

Одна вещь, которая меня раздражает, это то, что в событии по умолчанию Sender имеет тип объекта и поэтому почти всегда требует ручного приведения, прежде чем мы сможем его использовать.К счастью, поскольку VB теперь также поддерживает дисперсию в делегатах, мы можем обновить сигнатуру события таким образом, чтобы отправитель строго типизирован, см .: Параметр события;«отправитель как объект» или «отправитель как T»?

К сожалению, это не работает для существующих объявленных событий, отправители которых имеют тип объекта.

Теперь одно решение будетКонечно, чтобы создать поддельный EventHandler, который внутренне заботится о актерах для вас.Я сделал быстрый пример, см .:

struct EventHandler<TSender, TEventArgs>
    where TEventArgs: EventArgs
{
    private readonly Action<TSender, TEventArgs> _delegate;

    public EventHandler(Action<TSender, TEventArgs> @delegate)
    {
        if (@delegate == null)
            throw new ArgumentNullException("@delegate");

        _delegate = @delegate;
    }

    public static implicit operator EventHandler<TEventArgs>(EventHandler<TSender, TEventArgs> eventHandler)
    {
        return new EventHandler<TEventArgs>(eventHandler.Execute);
    }

    private void Execute(object sender, EventArgs e)
    {
        TSender typedSender = (TSender)sender;
        TEventArgs typedEventArgs = (TEventArgs)e;

        _delegate(typedSender, typedEventArgs);
    }
}

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

class Program
{
    event EventHandler<EventArgs> Test;

    static void Main(string[] args)
    {
        new Program().Main();
    }

    void Main()
    {
        Test += new EventHandler<Program, EventArgs>(TestEventHandler);

        Test(this, EventArgs.Empty);
    }

    void TestEventHandler(Program sender, EventArgs e)
    {
        throw new NotImplementedException();
    }
}

Теперь, если я действительно хочу это использовать, естьмного работы предстоит сделать.(Структура должна вести себя так же, как исходный делегат).Тем не менее, у меня есть ощущение, что либо уже есть отличная реализация, либо ее нет, поскольку есть некоторые серьезные недостатки, которые я упустил из виду.

Кто может ответить на мой вопрос выше?Любые другие советы?

Ответы [ 2 ]

2 голосов
/ 10 октября 2011

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

var original = (OriginalType)sender;

Кроме того, если ваш класс, ничто не мешает вам создать свой собственный делегат вместо делегата EventHandler

delegate void EventHandler<in TSender, in TArgs>(TSender sender, TArgs args);

Это противоречиво в аргументах и ​​отправителе

Люди обычно также имеют ссылку на объект, для которого вызывается событие, и поэтому им редко нужно извлекать вещь из отправителя.Возможно ли это в вашем случае?

Редактировать : поскольку вы упомянули, что имеете дело с событиями, которые не написаны вами, поэтому изменение делегата не подлежит сомнению, и вы, вероятно, неу него нет ссылки на объект, поэтому в этом случае вы должны прибегнуть к отправителю.Теперь в вашем решении вы не можете отписаться от события.Если вы измените его, чтобы поддержать это, то вам придется сохранить ссылку на эту вашу структуру, которая является большей работой, чем простое приведение.Я думаю, что кастинг по-прежнему самое чистое решение.

0 голосов
/ 10 октября 2011

Я считаю, что использование методов в качестве обработчиков событий сейчас плохо ООП практика. Теперь, когда у нас есть анонимные делегаты, нам просто не нужно создавать методы только для обработки событий. Создание метода обработчика событий, который может вызываться любым другим методом в классе, очень похож на общедоступность членов класса и позволяет любому классу вызывать что угодно.

В прошлом нам приходилось писать так:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += button_Click;
    }

    void button_Click(object sender, EventArgs e)
    {
        var button = (Button)sender; //Need to cast here
    }
}

Но теперь мы можем написать это:

public class Program
{
    public void Main()
    {
        var button = new Button();
        button.Click += (sender, e) =>
        {
            //Can use `button` here
            //Just ignore `sender`
        };
    }
}

Использование анонимных делегатов позволяет нам напрямую использовать ссылку «отправитель» события без необходимости приведения.

Гораздо чище объектно-ориентированное кодирование.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...