Когда использовать обратные вызовы вместо событий в c#?

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

Но когда Вы одобрили бы использование обратного вызова (т.е., передача в Func или Action), в противоположность представлению и использованию события?

ОБНОВЛЕНИЕ

То, что мотивировало этот вопрос, было следующей проблемой:

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

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

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

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

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

35
задан Stephen Kennedy 9 November 2018 в 18:12
поделиться

7 ответов

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

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

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

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

.
23
ответ дан 27 November 2019 в 06:36
поделиться

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

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

.
9
ответ дан 27 November 2019 в 06:36
поделиться

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

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

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

.
4
ответ дан 27 November 2019 в 06:36
поделиться

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

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

public void UseSum() {
    return sum(() => 10, () => 20);
}
4
ответ дан 27 November 2019 в 06:36
поделиться

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

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

.
27
ответ дан 27 November 2019 в 06:36
поделиться

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

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

4
ответ дан 27 November 2019 в 06:36
поделиться

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

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

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

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

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

28
ответ дан 27 November 2019 в 06:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: