Я прочитал две книги, тонны примеров. Они все еще не делают рядом ни с каким смыслом мне. Я мог, вероятно, написать некоторый код, который использует делегатов, но я понятия не имею почему. Действительно ли я - единственный с этой проблемой, или я - просто идиот? Если кто-либо может на самом деле объяснить мне, когда, где, и почему я на самом деле использовал бы делегата, я буду любить Вас навсегда.
Делегаты - это просто способ передать функцию в переменной.
Вы передаете делегированную функцию для выполнения обратного вызова. Например, при выполнении асинхронного ввода-вывода вы передаете делегированную функцию (функцию, которую вы написали с параметром делегата), которая будет вызываться, когда данные будут считаны с диска.
Делегат - это простой контейнер, который знает, где в памяти машины находится конкретный метод.
У всех делегатов есть метод Invoke (...)
, поэтому, когда у кого-то есть делегат, он может фактически выполнить его, не зная и не беспокоясь о том, что на самом деле делает этот метод.
Это особенно полезно для развязки. Фреймворки GUI были бы невозможны без этой концепции, потому что кнопка
просто не может ничего знать о вашей программе, в которой вы собираетесь ее использовать, поэтому она не может вызывать ваши методы самостоятельно, когда она нажата. Вместо этого вы должны указать ему, какие методы он должен вызывать при нажатии.
Думаю, вы знакомы с событиями и регулярно их используете. Поле события
на самом деле представляет собой список таких делегатов (также называемый многоадресным делегатом). Возможно, все станет яснее, когда мы посмотрим, как мы могли бы «моделировать» события в C #, если бы в нем не было ключевого слова event
, а были только делегаты (без многоадресной рассылки):
public class Button : Rectangle
{
private List<Delegate> _delegatesToNotifyForClick = new List<Delegate>();
public void PleaseNotifyMeWhenClicked(Delegate d)
{
this._delegatesToNotifyForClick.Add(d);
}
// ...
protected void GuiEngineToldMeSomeoneClickedMouseButtonInsideOfMyRectangle()
{
foreach (Delegate d in this._delegatesToNotifyForClick)
{
d.Invoke(this, this._someArgument);
}
}
}
// Then use that button in your form
public class MyForm : Form
{
public MyForm()
{
Button myButton = new Button();
myButton.PleaseNotifyMeWhenClicked(new Delegate(this.ShowMessage));
}
private void ShowMessage()
{
MessageBox.Show("I know that the button was clicked! :))))");
}
}
Надеюсь, я смогу помочь маленький. ; -)
Как уже упоминалось другими людьми, делегаты удобны для обратных вызовов. Они полезны и для множества других вещей. Например, в игре, над которой я недавно работал, пули при попадании делают разные вещи (некоторые наносят урон, некоторые фактически увеличивают здоровье человека, которого они поражают, некоторые не наносят урона, но отравляют цель и так далее). Классический способ сделать это в ООП - это базовый класс маркеров и загрузка подклассов
Bullet
DamageBullet
HealBullet
PoisonBullet
DoSomethingElseBullet
PoisonAndThenHealBullet
FooAndBarBullet
....
. С помощью этого шаблона я должен определять новый подкласс каждый раз, когда мне нужно какое-то новое поведение в пуле, что является беспорядком и приводит к много дублированного кода. Вместо этого я решил это с делегатами. У пули есть делегат OnHit, который вызывается, когда пуля попадает в объект, и, конечно, я могу сделать этот делегат чем угодно. Итак, теперь я могу создавать такие маркеры
new Bullet(DamageDelegate)
, что, очевидно, намного лучше.
В функциональных языках можно встретить гораздо больше подобных вещей.
Может быть, это поможет:
Назначение делегатов в том, что вы можете иметь переменные / поля / параметры / properties (события), которые «удерживают» функцию. Это позволяет вам сохранять / передавать конкретную функцию, которую вы выбираете во время выполнения. Без него каждый вызов функции должен быть исправлен во время компиляции.
Синтаксис, включающий делегаты (или события), поначалу может быть немного пугающим, на это есть две причины:
простые указатели на функции, как в C / C ++, не будут типобезопасными, в .NET компилятор фактически создает вокруг него класс, а затем пытается скрыть это насколько возможно.
Делегаты являются краеугольным камнем LINQ, и в C # 1 происходит резкая эволюция от указания всего в C # 1 через анонимные методы (C # 2) до лямбда-выражений (C # 3).
Просто ознакомьтесь с 1 или 2 стандартными шаблонами.