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

13
задан AspOnMyNet 16 February 2010 в 18:32
поделиться

9 ответов

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

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

ОБНОВЛЕНИЕ

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

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

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

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

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

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

4
ответ дан 1 December 2019 в 21:52
поделиться

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

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

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

6
ответ дан 1 December 2019 в 21:52
поделиться

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

5
ответ дан 1 December 2019 в 21:52
поделиться

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

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

3
ответ дан 1 December 2019 в 21:52
поделиться

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

Например:

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

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

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

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

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

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

1
ответ дан 1 December 2019 в 21:52
поделиться

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

2
ответ дан 1 December 2019 в 21:52
поделиться

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

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

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

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

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

При подходе издателя/подписчика у вас могут быть следующие события 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.

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

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

1
ответ дан 1 December 2019 в 21:52
поделиться

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

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

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

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

1
ответ дан 1 December 2019 в 21:52
поделиться

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

Обновите все соответствующие записи предложений для этого обвинения, установив дату их статуса на дату события. Если обвинение снимается с переадресацией, релевантными предложениями являются предложения с типом предложения «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% кода, и их легко обновлять, когда меняются условия запуска.

1
ответ дан 1 December 2019 в 21:52
поделиться
Другие вопросы по тегам:

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