Делегаты, почему?
Возможные дубликаты:
Когда вы будете использовать делегатов на С#? Цель делегатов
Я видел много вопросов относительно использования делегатов. Я все еще не понимаю, где и ПОЧЕМУ вы бы использовали делегатов вместо прямого вызова метода.
Я слышал эту фразу много раз: "Затем объект-делегат может быть передан в код, который может вызвать ссылочный метод, без необходимости знать во время компиляции, какой метод будет вызван".
Я не понимаю, как это утверждение правильно.
Я написал следующие примеры. Скажем, у вас есть 3 метода с одинаковыми параметрами:
public int add(int x, int y)
{
int total;
return total = x + y;
}
public int multiply(int x, int y)
{
int total;
return total = x * y;
}
public int subtract(int x, int y)
{
int total;
return total = x - y;
}
Теперь я объявляю делегата:
public delegate int Operations(int x, int y);
Теперь я могу сделать еще один шаг: объявить обработчик для использования этого делегата (или вашего делегата напрямую)
Вызов делегата:
MyClass f = new MyClass();
Operations p = new Operations(f.multiply);
p.Invoke(5, 5);
или вызов с помощью обработчика
f.OperationsHandler = f.multiply;
//just displaying result to text as an example
textBoxDelegate.Text = f.OperationsHandler.Invoke(5, 5).ToString();
В обоих случаях я вижу, что мой "множимый" метод указан. Почему люди используют фразу "изменить функциональность во время выполнения" или выше?
Почему делегаты используются, если каждый раз, когда я объявляю делегата, ему нужен метод для указания? и если ему нужен метод, на который указывает, почему бы просто не вызвать этот метод напрямую? Мне кажется, что я должен написать больше кода для использования делегатов, чем просто использовать функции напрямую.
Может кто-нибудь, пожалуйста, дайте мне реальную ситуацию в мире? Я полностью смущен.
Ответы
Ответ 1
Изменение функциональности во время выполнения - это не то, что выполняют делегаты.
В принципе, делегаты экономят вас от печатания.
Например:
class Person
{
public string Name { get; }
public int Age { get; }
public double Height { get; }
public double Weight { get; }
}
IEnumerable<Person> people = GetPeople();
var orderedByName = people.OrderBy(p => p.Name);
var orderedByAge = people.OrderBy(p => p.Age);
var orderedByHeight = people.OrderBy(p => p.Height);
var orderedByWeight = people.OrderBy(p => p.Weight);
В приведенном выше коде p => p.Name
, p => p.Age
и т.д. - все лямбда-выражения, которые вычисляют делегаты Func<Person, T>
(где T
- string
, int
, double
и double
, соответственно).
Теперь рассмотрим, как мы могли бы достичь вышеуказанного без делегатов. Вместо того, чтобы метод OrderBy
принял параметр делегата, нам пришлось бы отказаться от общности и определить эти методы:
public static IEnumerable<Person> OrderByName(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByAge(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByHeight(this IEnumerable<Person> people);
public static IEnumerable<Person> OrderByWeight(this IEnumerable<Person> people);
Это будет полностью сосать. Я имею в виду, во-первых, код стал бесконечно менее многоразовым, поскольку он применим только к коллекциям типа Person
. Кроме того, мы должны копировать и вставлять один и тот же код четыре раза, изменяя только одну или две строки в каждой копии (где ссылается соответствующее свойство Person
, иначе все будет выглядеть одинаково)! Это быстро стало бы недостижимым беспорядком.
Таким образом, делегаты позволяют сделать ваш код более многократно используемым и более удобным для обслуживания, абстрагируя некоторые типы поведения внутри кода, который можно включать и выключать.
Ответ 2
. NET-делегаты: С# Bedtime Story
Ответ 3
Делегаты чрезвычайно полезны, особенно после введения linq и закрытия.
Хорошим примером является функция "Где", один из стандартных методов linq. 'Where' принимает список и фильтр и возвращает список элементов, соответствующих фильтру. (Аргумент фильтра - это делегат, который принимает T и возвращает логическое значение.)
Поскольку он использует делегат для указания фильтра, функция Where чрезвычайно гибкая. Например, вам не нужны разные функции для фильтрации нечетных чисел и простых чисел. Вызывающий синтаксис также очень краток, что бы не было, если вы использовали интерфейс или абстрактный класс.
Более конкретно: если взять делегата, вы можете написать это:
var result = list.Where(x => x != null);
...
вместо этого:
var result = new List<T>();
foreach (var e in list)
if (e != null)
result.add(e)
...
Ответ 4
Почему делегаты используются, если каждый раз, когда я объявить делегата, ему нужен метод указать на? и если ему нужен метод чтобы указать, почему бы просто не называть это метод напрямую?
Как и интерфейсы, делегаты позволяют вам разделить и обобщить ваш код. Обычно вы используете делегатов, когда заранее не знаете, какие методы вы хотите выполнить - когда вы только знаете, что вы захотите выполнить что-то, что соответствует определенной сигнатуре.
Например, рассмотрим класс таймера, который будет выполнять некоторый метод через регулярные интервалы:
public delegate void SimpleAction();
public class Timer {
public Timer(int secondsBetweenActions, SimpleAction simpleAction) {}
}
Вы можете подключить что-либо к этому таймеру, чтобы вы могли использовать его в любом другом проекте или приложениях, не пытаясь предсказать, как вы его используете, и не ограничивая его использование небольшим количеством сценариев, о которых вы думаете Теперь.
Ответ 5
Позвольте мне привести пример. Если ваш класс предоставляет event
, ему может быть назначено некоторое количество делегатов во время выполнения, которое будет вызываться, чтобы сигнализировать, что что-то произошло. Когда вы написали класс, вы понятия не имели, какие делегаты будут работать. Вместо этого это определяется тем, кто использует ваш класс.
Ответ 6
Чтобы дать конкретный пример, особенно недавнее использование делегата для меня было SendAsync()
в System.Net.Mail.SmtpClient
. У меня есть приложение, которое отправляет тонны и тонны электронной почты, и наблюдается заметное повышение производительности, ожидающее, когда сервер Exchange примет сообщение. Однако необходимо было зарегистрировать результат взаимодействия с этим сервером.
Итак, я написал метод делегата для обработки этого ведения журнала и передал его SendAsync()
(мы ранее использовали только Send()
) при отправке каждого сообщения. Таким образом, он может перезвонить делегату для регистрации результата, а потоки приложений не ждут завершения взаимодействия, прежде чем продолжить.
То же самое можно сказать о любом внешнем IO, в котором вы хотите продолжить приложение, не дожидаясь завершения взаимодействия. Прокси-классы для веб-сервисов и т.д. Используют это.
Ответ 7
Один пример, когда требуется delegate
, - это когда вам нужно изменить элемент управления в потоке пользовательского интерфейса, и вы работаете в другом потоке. Например,
public delegate void UpdateTextBox(string data);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
...
Invoke(new UpdateTextBox(textBoxData), data);
...
}
private void textBoxData(string data)
{
textBox1.Text += data;
}
Ответ 8
В вашем примере, как только вы назначили делегата переменной, вы можете передать его, как и любую другую переменную. Вы можете создать метод, принимающий делегат в качестве параметра, и он может вызвать делегата, не требуя знать, где этот метод действительно объявлен.
private int DoSomeOperation( Operations operation )
{
return operation.Invoke(5,5);
}
...
MyClass f = new MyClass();
Operations p = new Operations(f.multiply);
int result = DoSomeOperation( p );
Делегаты делают методы в вещах, которые вы можете передавать так же, как и int. Вы могли бы сказать, что переменные не дают вам ничего лишнего, потому что в
int i = 5;
Console.Write( i + 10 );
вы видите, что значение 5 указано, поэтому вы можете просто сказать Console.Write( 5 + 10 )
. Это правда в этом случае, но оно не хватает преимуществ за возможность сказать
DateTime nextWeek = DateTime.Now.AddDays(7);
вместо того, чтобы определять метод specificc DateTime.AddSevenDays()
и метод AddSixDays
и т.д.
Ответ 9
Вы можете использовать делегаты для реализации подписки и eventHandlers.
Вы также можете (ужасно) использовать их, чтобы обойти круговые зависимости.
Или, если у вас есть механизм вычисления, и есть много возможных вычислений, тогда вы можете использовать делегат параметров вместо многих различных вызовов функций для вашего движка.
Ответ 10
Вы читали http://msdn.microsoft.com/en-us/library/ms173171(VS.80).aspx?
Ответ 11
Используя ваш пример Operations
, представьте калькулятор с несколькими кнопками.
Вы можете создать класс для своей кнопки, как этот
class CalcButton extends Button {
Operations myOp;
public CalcButton(Operations op) {
this.myOp=op;
}
public void OnClick(Event e) {
setA( this.myOp(getA(), getB()) ); // perform the operation
}
}
а затем, когда вы создаете кнопки, вы можете создать их с другой операцией
CalcButton addButton = new CalcButton(new Operations(f.multiply));
Это лучше по нескольким причинам. Вы не копируете код в кнопках, они носят общий характер.
У вас может быть несколько кнопок, которые имеют одну и ту же операцию, например, на разных панелях или в меню. Вы можете изменить операцию, связанную с кнопкой "на лету".
Ответ 12
Делегаты используются для решения проблемы с доступом. Когда вы хотите иметь объект foo, который должен вызвать метод frob объекта, но не имеет доступа к методу frob.
Объект goo имеет доступ как к foo, так и к bar, поэтому он может связывать его вместе с помощью делегатов. Обычно бары и сливки часто являются одним и тем же объектом.
Например, класс Button обычно не имеет доступа к классу, определяет метод Button_click.
Итак, теперь, когда у нас есть, мы можем использовать его для многих других вещей, кроме как только событий. Асинхронные паттерны и Linq - это два примера.
Ответ 13
Похоже, что многие ответы связаны с встроенными делегатами, которые, на мой взгляд, легче понять, чем то, что я назову "классическими делегатами".
Ниже приведен мой пример того, как делегаты позволяют классу потребления изменять или дополнять поведение (эффективно добавляя "крючки", чтобы потребитель мог делать что-либо до или после критического действия и/или вообще предотвращать это поведение). Обратите внимание, что вся логика принятия решений предоставляется вне класса StringSaver
. Теперь рассмотрим, что могут быть 4 разных потребителя этого класса - каждый из них может реализовать свою собственную логику Verification
и Notification
, или нет, если это необходимо.
internal class StringSaver
{
public void Save()
{
if(BeforeSave != null)
{
var shouldProceed = BeforeSave(thingsToSave);
if(!shouldProceed) return;
}
BeforeSave(thingsToSave);
// do the save
if (AfterSave != null) AfterSave();
}
IList<string> thingsToSave;
public void Add(string thing) { thingsToSave.Add(thing); }
public Verification BeforeSave;
public Notification AfterSave;
}
public delegate bool Verification(IEnumerable<string> thingsBeingSaved);
public delegate void Notification();
public class SomeUtility
{
public void SaveSomeStrings(params string[] strings)
{
var saver = new StringSaver
{
BeforeSave = ValidateStrings,
AfterSave = ReportSuccess
};
foreach (var s in strings) saver.Add(s);
saver.Save();
}
bool ValidateStrings(IEnumerable<string> strings)
{
return !strings.Any(s => s.Contains("RESTRICTED"));
}
void ReportSuccess()
{
Console.WriteLine("Saved successfully");
}
}
Я думаю, дело в том, что метод, которому точки делегата необязательно входит в класс, подвергает члену делегата.