Подтвердить что ты не робот

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

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

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

UPDATE

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

В терминах проектирования OO и сочетания классов не существует большой разницы между интерфейсом обратного вызова и событием.

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

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

Ответ 7

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

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

public void UseSum() {
    return sum(() => 10, () => 20);
}

Ответ 8

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

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

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

И, MADN также дает нам введение различия между ними. нажмите здесь

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

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

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

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