Объясните делегатам .NET

Итак, я читаю MSDN и Stack Overflow. Я понимаю, что делает Делегат действий в целом, но он не щелкает, независимо от того, сколько примеров я делаю. В общем, то же самое касается идеи делегатов. Вот мой вопрос. Когда у вас есть такая функция:

public GetCustomers(Action<IEnumerable<Customer>,Exception> callBack)
{
}

Что это, и что мне передать?

Ответ 1

он ожидает функцию, которая принимает IEnumerable и Exception и возвращает void.

void SendExceptionToCustomers(IEnumerable<Customer> customers, Exception ex) {
   foreach(var customer in customers)
      customer.SendMessage(ex.Message);
}

GetCustomers(SendExceptionToCustomers);

btw, GetCustomers кажется ужасным именем для этой функции - он просит действия, поэтому его больше похоже на DoSomethingToCustomers

ИЗМЕНИТЬ в ответ на комментарий


Хорошо. Делает смысл. Так почему же даже беспокоиться о наличии функции GetCustomer? Не могу ли я сделать то же самое с вашей функцией, если бы я просто переименовал его в GetCustomer?

Хорошо, что происходит здесь, вызывающий может указать какое-то действие. Предположим, что GetCustomers реализованы следующим образом:

public void GetCustomers(Action<Enumerable<Customer>, Exception> handleError) {
    Customer[] customerlist =  GetCustomersFromDatabase();
    try {
        foreach(var c in customerList) 
            c.ProcessSomething()
    } catch (Exception e) {
        handleError(customerList, e);
    }
}

то вы можете вызвать Getcustomers откуда-нибудь из командной строки и передать его

GetCustomers((list, exception) => { 
    Console.WriteLine("Encountered error processing the following customers");
    foreach(var customer in list) Console.WriteLine(customer.Name);
    Console.WriteLine(exception.Message);
}); 

в то время как вы могли бы вызвать GetCustomers из удаленного приложения, например, и передать его

Getcustomers((list, exception) => { 
    // code that emails me the exception message and customer list
})


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

Ответ 2

Делегат - это класс, который указывает на одну или несколько функций. Можно вызвать экземпляр делегата, который вызовет функцию (ы), на которую он указывает.

В вашем случае функция GetCustomers принимает вторую функцию как параметр.

Вторая функция должна принимать два параметра типа IEnumerable<Customer> и Exception.

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

Например:

static void GetCustomersCallback(IEnumerable<Customer> customers, Exception ex) {
    //Do something...
}

//Elsewhere:
GetCustomers(new Action<IEnumerable<Customer>,Exception>(GetCustomersCallback));

Этот вызов создает новый экземпляр делегата, который указывает на функцию GetCustomersCallback, и передает делегат функции GetCustomers. GetCustomers предположительно вызовет обратный вызов после завершения загрузки клиентами и передаст загруженных клиентов в качестве параметра.
Вы также можете оставить экземпляр делегата и передать функцию напрямую:

GetCustomers(GetCustomersCallback);

Ответ 3

вы можете вызвать его с помощью лямбда

GetCustomers((cust, ex) => {
    //do something here}
);

Ответ 5

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

Типичная подпись может быть сделана общей с использованием общих возможностей С#.

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

Делегат имени плохо выбран (если вы не думаете обо всех наших приложениях, управляемых фреймворками). Потому что использование не всегда "делегируется". Часто это код, на который вы делегировали определенную ответственность (итератор, скажем), который вызывает "делегат", который вы ранее отправили. Вот почему вы часто видите термин "обратный вызов".

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

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

Ответ 6

Просто? Указатели функций

Ответ 7

Вы передадите ему метод void, который принимает параметры IEnumerable и Exception как параметры...

Скажите, что у вас есть этот метод:

public void DoSomeStuffWithCustomers(
  IEnumerable<Customer> customers, Exception exception)
{     
}

Вы бы вызвали метод GetCustomers следующим образом:

GetCustomers(DoSomeStuffWithCustomers);

Ответ 8

Они сбивали меня с ума, пока я не прочитал:

В этой второй книге рассказывается о java и не упоминается о делегатах, но она хорошо объясняет проблему, с которой помогает делегат: связь между классами.