Когда использовать обратные вызовы вместо событий в C #? - PullRequest
35 голосов
/ 07 января 2010

Простите, если это глупый вопрос, я признаю, что не особо задумывался об этом.

Но когда вы предпочитаете использовать обратный вызов (т. Е. Передать функцию или действие), а не выставлять и использовать событие?

UPDATE

Что мотивировало этот вопрос была следующая проблема:

У меня есть класс ThingsHandler, который может быть связан с ThingEditor. ThingsHandler обрабатывает список Вещи, знает их порядок, который является «текущим», когда новый те добавлены или удалены и т. д.

ThingEditors может просто изменить один вещь.

ThingsHandler должен предупредить ThingEditor, когда пользователь выбирает новая вещь для редактирования, и ThingEditor должен предупредить ThingsHandler, когда пользователь говорит 'Сделано'.

Меня беспокоит то, что эти два класса содержат ссылки друг на друга - хотя я думаю, что это неизбежно - или привязку к событиям в обоих направлениях. Я задавался вопросом, было ли использование обратного вызова в одном направлении «чище».

Я подозреваю, что для этого есть шаблон, и смиренно извиняюсь за свое невежество (и лень).

Ответы [ 8 ]

28 голосов
/ 07 января 2010

Хотя другие ответы пока кажутся разумными, я бы взял более философский подход.

Класс - это механизм , который моделирует определенный тип вещей в определенной области. При написании внутренних деталей класса очень легко сопоставить детали реализации механизма с моделируемой семантикой . Краткий пример того, что я имею в виду:

class Giraffe : Mammal, IDisposable
{
    public override void Eat(Food f) { ... }
    public void Dispose() { ... }
}

Обратите внимание на то, как мы связали моделируемую реальность (жираф - это вид млекопитающего, жираф ест пищу) с деталями реализации (экземпляр жирафа - это объект, от которого можно избавиться с помощью «использование» заявление). Я гарантирую, что если вы пойдете в зоопарк, вы никогда не увидите жирафа, выбрасываемого с помощью оператора using. Мы перепутали уровни здесь, что вызывает сожаление.

Я пытаюсь использовать события (и свойства) как часть семантической модели и использую методы обратного вызова (и поля) как часть механизма . Я бы сделал GaveBirth событием Giraffe, поскольку это часть модели поведения жирафа в реальном мире, которую мы пытаемся запечатлеть. Если бы у меня был какой-то механизм, например, скажем, я хотел реализовать алгоритм обхода дерева обхода по порядку, который обходил семейное древо жирафов и вызывал метод обратно для каждого, то я бы сказал, что это явно механизм, а не модели, и сделайте это обратным вызовом, а не пытайтесь вставить это в модель события.

26 голосов
/ 07 января 2010

Я использую обратные вызовы в нескольких случаях, когда я знаю, что он будет когда-либо срабатывать один раз, а обратный вызов специфичен для одного вызова метода (а не для экземпляра объекта) - например, в качестве возвращаемой части асинхронного метода.

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

23 голосов
/ 07 января 2010

Обычно я использую обратный вызов, если требуется , тогда как событие используется, когда оно должно быть необязательным. Не показывайте событие, если вы ожидаете, что всегда будет что-то слушать.

Рассмотрим следующее:

public class MyClass_Event
{
    public event EventHandler MakeMeDoWork;

    public void DoWork()
    {
        if (MakeMeDoWork == null)
            throw new Exception("Set the event MakeMeDoWork before calling this method.");
        MakeMeDoWork(this, EventArgs.Empty);
    }
}

против

public class MyClass_Callback
{
    public void DoWork(EventHandler callback)
    {
        if (callback == null)
            throw new ArgumentException("Set the callback.", "callback"); // better design
        callback(this, EventArgs.Empty);
    }
}

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

9 голосов
/ 07 января 2010

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

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

4 голосов
/ 07 января 2010

Я бы использовал Func или Action, когда я собираюсь вызвать функцию один раз или использовать лямбда-выражение.

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

4 голосов
/ 07 января 2010

С точки зрения дизайна ОО и связи классов нет большой разницы между интерфейсом обратного вызова и событием.

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

Что бы вы ни использовали, используйте их последовательно в кодовой базе!

3 голосов
/ 07 января 2010

Один пример - когда обратный вызов должен что-то возвращать. Например. (глупый пример):

public int Sum(Func<int> callbackA, Func<int> callbackB) {
    return callbackA() + callbackB();
}

public void UseSum() {
    return sum(() => 10, () => 20);
}
0 голосов
/ 19 октября 2015

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

Итак, что вы подразумеваете под "обратным вызовом" или "обработчиком событий"?

Согласно MSDN : функция обратного вызова - это код в управляемом приложении, который помогает неуправляемой функции DLL завершить задачу.

И MADN также дает нам представление о разнице между ними. нажмите здесь

Обратные вызовы - это точки расширения, которые позволяют платформе перезванивать в код пользователя через делегата. Эти делегаты обычно передаются в платформу через параметр метода.

События - это особый случай обратных вызовов, который поддерживает удобный и согласованный синтаксис для предоставления делегата (обработчик события). Кроме того, завершение операторов Visual Studio и разработчики предоставляют помощь в использовании основанных на событиях API

Кроме того, в некоторых книгах, таких как в этой книге , автор, похоже, говорил то же самое с MSDN.

Поэтому, на мой взгляд, нельзя сказать использовать обратные вызовы вместо событий в C #.

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