Каковы некоторые общие сценарии, в которых делегаты должны использоваться?

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

спасибо

ОТВЕТ НА ПОЛЬЗОВАТЕЛЮ KVB POST:

а)

В основном вы можете использовать делегаты, где бы вы ни использовали интерфейс с одним методом.

Думаю, я несколько понимаю следующее:

  • Класс C может определить метод C.M, который в качестве аргумента примет интерфейс IM. Этот интерфейс будет определять метод IM.A, и поэтому любому, кто хочет вызвать C.M, потребуется реализовать этот интерфейс.

  • В качестве альтернативы метод CM может принимать (вместо интерфейса IM) в качестве аргумента делегат D с той же сигнатурой как метод IM.A.

Но я не понимаю, почему Cant CM также использует в качестве параметра делегат D, даже если наш интерфейс IM определяет несколько других методов кроме того метод A? Таким образом, другие методы класса C могут потребовать в качестве аргумента интерфейса IM, но CM может потребоваться делегат D (при условии, что CM нужно только вызвать метод A, а не любой другой метод, определенный в IM)?

b)

var list = new List<int>(new[] { 1, 2, 3 });
var item = list.Find(i => i % 2 == 0);
  • Является ли приведенный выше код примером того, что пользователь вызывает jpbochi (см. ее/его сообщение в этой теме) инъекции зависимостей?

  • Я предполагаю, что указанный выше код не может быть реализован с использованием событий вместо "чистых" делегатов?

Ответы

Ответ 1

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

var list = new List<int>(new[] { 1, 2, 3 });
var item = list.Find(i => i % 2 == 0);

В отличие от:

var list = new List<int>(new[] { 1, 2, 3 });
list.Find(new DivisibleBy2Finder());

// Somewhere far away
private class DivisibleBy2Finder : IFinder<int> {
    public bool Matches(int i) {
        return i % 2 == 0;
    }
}

UPDATE

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

  • Вы можете создать делегат из группы методов. Например, вы можете создать делегат Action<string> следующим образом: Action<string> action = new Action<string>(Console.WriteLine);. Это создает делегат, который будет печатать свой аргумент на консоли, когда ему передается строка. Хотя это позволяет эффективно передавать методы, эти методы уже должны быть определены в каком-либо классе.
  • Вы можете создать анонимный делегат. Для меня это ключевая причина, по которой делегаты особенно полезны в С#. Некоторые другие языки используют разные конструкции, чтобы инкапсулировать бит логики в своей точке использования (например, Java имеет анонимные классы). С# не имеет анонимных классов, поэтому, если вы хотите создать немного автономной логики, которая может быть передана другому методу, использование анонимного делегата (или нескольких анонимных делегатов) часто является лучшим подходом на С#. Это то, что я пытался проиллюстрировать с моим примером в моем первоначальном сообщении.

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

  • Делегаты могут быть добавлены или удалены из события. Эти делегаты будут вызываться, когда событие запускается/вызывается.
  • Только в контексте класса, в котором объявлено событие, событие можно рассматривать как стандартный делегат, что означает, что он может быть вызван, и его список вызовов может быть проверен/обработан.

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

Надеюсь, что это поможет немного прояснить ситуацию.

Ответ 2

Если вы хотите предоставить функцию, которая будет выполнена на каком-либо событии. Вы передаете событию Event обработчик делегата функции, которая должна быть выполнена. Они великолепны, когда вы программируете на основе событий.

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

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

Ответ 3

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

Ответ 4

Реализация обратных вызовов и прослушивателей событий.

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

Ответ 5

асинхронные обратные вызовы - еще один хороший пример

Ответ 6

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

Отредактировано для добавления:

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

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

Ответ 7

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

Например:

  • У меня есть два элемента управления TabControlA и TabControlB на MainForm. Их код находится в отдельных библиотеках, которые являются зависимостями MainForm.

  • TabControlA имеет общедоступный метод SetShowMessage, который устанавливает частный член с именем ShowMessage любому делегату (типа Action <string> , say).

  • Когда MainForm загружается, он может получить настройки, вызвав TabControlA.SetShowMessage(TabControlB.PrettyShowingFunction), чтобы подключить (только эту часть) TabControlB к (только та часть) TabControlA.

  • Теперь внутренне TabControlA может проверить, не является ли ShowMessage ненулевым, и вызвать ShowMessage ( "Hurray, сообщение, которое будет отображаться на TabControlB!" ), и теперь вызывается TabControlB.PrettyShowingFunction, позволяя TabControlA взаимодействовать с TabControlB, который может отображать это сообщение.

  • Это может быть расширено, чтобы TabControlC мог делать то же самое и показывать сообщения на TabControlB и т.д.

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

Ответ 8

Мне нравятся делегаты и события (они идут рука об руку) для разделения озабоченности (SOC).

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

События для издателей и подписчики получают уведомления.

Как это работает? Вот краткий пример.

Скажем, ваш код должен иметь действительный ввод перед обработкой заказа. В процедурном подходе ваш код (контроллер) может инициировать метод "порядок". Затем заказ проверяет, а затем отправляет или отклоняет...

В подходе издателя/подписчика могут быть следующие события OrderSubmitted, OrderValidated и OrderRejected. Это будут ваши издатели. Затем у вас есть несколько подписчиков, ValidateOrder, CommitOrder и RejectOrder... ValidateOrder будет подписаться на OrderSubmitted, CommitOrder подписывается на OrderValidated и, наконец, RejectOrder подписывается на OrderRejected.

В качестве параметра для ваших событий вы передаете заказ. Тогда серия событий будет...

Контроллер получает заказ. В коде предполагается, что проверка нулевого события имеет место...

void Init()
{
    ValidateOrder += SomeValidateMethod;
    CommitOrder += SomeCommitMethod;
    RejectOrder += SomeRejectMethod;
}

void OrderReceived(Order o)
{
  OrderEventArgs OEA = new OrderEventArgs(o);

  ValidateOrder(this, OEA);

  if (OEA.OrderIsValid)
      CommitOrder(this, OEA);
  else
      RejectOrder(this, OEA);
}

Просто так, у нас есть некоторые события. Теперь, почему мы используем события/делегаты? Скажем, код для отклонения заказа обновляет базу данных, без проблем. Кто-то говорит, позвольте почтовому клиенту, когда заказ отклонен. Вам нужно реорганизовать SomeRejectMethod? Нет, вы можете просто создать новый метод EmailOrderRejected и добавить его в качестве подписчика на событие RejectOrder.

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

Попытаюсь продолжить некоторые ссылки позже, удачи.

Ответ 9

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

Обновите все соответствующие записи предложений для этого платежа, установив дату своего статуса на дату события. Если плата распределяется с направлением перенаправления, соответствующими предложениями являются предложения с типом предложения "DV", "DCV" или "DVS". Если обвинение распределено с отложенным вводом решения, соответствующими предложениями являются предложения с типом предложения "DEJ". Игнорируйте все другие обвинения и предложения.

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

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

private static readonly HashSet<string> DiversionTypes = 
    new HashSet() { "DV", "DCV", "DVS" };
private bool SentenceIsDiversion(DataRow r) { return (DiversionTypes.Contains(r.Field<string>("Type"))); }

private bool SentenceIsDEJ(DataRow r) { return r.Field<string>("Type") == "DEJ"; }

// Map charge disposition codes for diversion and DEJ to predicates that
// test sentence rows for relevance.  Only sentences for charges whose disposition
// code is in this map and who are described by the related predicate should be
// updated.
private static readonly Dictionary<string, Func<DataRow, bool>> DispoToPredicateMap =
    new Dictionary<string, Func<DataRow, bool>>
{
   { "411211", SentenceIsDiversion },
   { "411212", SentenceIsDiversion },
   { "411213", SentenceIsDEJ },
   { "411214", SentenceIsDEJ },
}

Что делает логику обновления выглядеть так:

string disposition = chargeRow.Field<string>("Disposition");
if (DispoToPredicateMap.ContainsKey(disposition))
{
    foreach (DataRow sentenceRow in chargeRow.GetChildRows("FK_Sentence_Charge"))
    {
       if (DispoToPredicateMap[disposition](sentenceRow))
       {
          sentenceRow.SetField("StatusDate", eventDate);
       }
    }
}

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