Я хочу реализовать шаблон Observer на VB.NET, C# или другом первоклассном языке .NET. Я слышал, что для этого можно использовать делегатов, но не могу понять, почему они предпочтительнее простых старых интерфейсов, реализованных на наблюдателях. Так,
Можете ли вы привести пример ситуации, когда у вас есть это сомнение? Я не уверен, что понимаю, о чем вы просите.
возможный дубликат Делегаты - это не просто сокращенные интерфейсы?





По сути, делегат передает ссылку на метод, а не на объект ... Интерфейс - это ссылка на подмножество методов, реализованных объектом ...
Если в каком-либо компоненте вашего приложения вам нужен доступ к более чем одному методу объекта, тогда определите интерфейс, представляющий это подмножество методов объекта, и назначьте и реализуйте этот интерфейс для всех классов, которые вам, возможно, потребуется передать этому компонент ... Затем передайте экземпляры этих классов через этот интерфейс, а не через их конкретный класс ..
Если, otoh, в каком-то методе или компоненте, все, что вам нужно, это один из нескольких методов, которые могут быть в любом количестве разных классов, но все имеют одну и ту же сигнатуру, тогда вам нужно использовать делегат.
Я слышал, как об этом говорят некоторые «евангелисты событий», и они говорят, что чем больше разрозненных событий, тем лучше.
Предпочтительно, чтобы источник события никогда не знал о прослушивателях событий, а прослушиватель событий никогда не должен заботиться о том, кто инициировал событие. Сегодня все обстоит иначе, потому что в прослушивателе событий вы обычно получаете исходный объект события.
С учетом сказанного делегаты - идеальный инструмент для этой работы. Они позволяют разделить источник событий и наблюдатель событий, поскольку источнику событий не нужно хранить список всех объектов-наблюдателей. Он только хранит список «указателей на функции» (делегатов) наблюдателей. Поэтому я считаю это большим преимуществом перед интерфейсами.
Посмотри на это с другой стороны. Какое преимущество будет иметь пользовательский интерфейс по сравнению со стандартным способом, который поддерживается языком как в синтаксисе, так и в библиотеке?
Конечно, существуют случаи являются, когда это индивидуальное решение может иметь преимущества, и в таких случаях вы должны его использовать. Во всех остальных случаях используйте наиболее каноническое решение. Это меньше работы, более интуитивно понятно (потому что этого ожидают пользователи), имеет большую поддержку со стороны инструментов (включая IDE), и, скорее всего, компилятор обрабатывает их по-другому, что приводит к более эффективному коду.
Не изобретайте велосипед (если текущая версия не работает).
У метода "интерфейса наблюдателя" есть несколько преимуществ, которые я вижу: -1- при слабой ссылке на наблюдателя можно выполнить на нем его метод "наблюдения"; в .net 2.x нет средств для сохранения делегата без сохранения сильной ссылки (или «воссоздания» слабой ссылки в делегат). -2- С шаблоном «наблюдатель / интерфейс» можно спросить наблюдателей, «интересуются» ли они по-прежнему, и удалить их, если нет. При использовании делегатов такое удаление намного сложнее и требует большей координации между издателем событий и подписчиком.
@supercat: ваша точка 1 верна лишь частично - вы можете реализовать свои собственные классы, подобные делегатам, для использования со слабыми ссылками - это особенно важно для событий и поддерживается синтаксисом custom event. Я не уверен в вашем втором пункте - разве это не то, что наблюдатель может решить в любом случае, как только он будет вызван? Я не вижу проблемы.
Нет хорошего способа реализовать поведение слабого события без особого взаимодействия с кодом, который его использует. Хотя обработчик событий, который получает событие, в котором он не заинтересован, может попытаться отказаться от подписки, попытка отказа от подписки из обработчика событий потенциально может вызвать взаимоблокировку, в зависимости от того, как реализовано событие. Я бы не подумал, что обработчику безопасно предположить, что он может отказаться от подписки в рамках запущенного события.
Есть два места, где вы можете использовать делегатов в шаблоне наблюдателя. Поскольку я не уверен, о каком из них вы имеете в виду, я постараюсь ответить на оба вопроса.
Первый - использовать делегатов в теме вместо списка IObservers. Этот подход кажется намного более понятным при обработке многоадресной рассылки, поскольку в основном у вас есть
private delegate void UpdateHandler(string message);
private UpdateHandler Update;
public void Register(IObserver observer)
{
Update+=observer.Update;
}
public void Unregister(IObserver observer)
{
Update-=observer.Update;
}
public void Notify(string message)
{
Update(message);
}
вместо
public Subject()
{
observers = new List<IObserver>();
}
public void Register(IObserver observer)
{
observers.Add(observer);
}
public void Unregister(IObserver observer)
{
observers.Remove(observer);
}
public void Notify(string message)
{
// call update method for every observer
foreach (IObserver observer in observers)
{
observer.Update(message);
}
}
Если вам не нужно делать что-то особенное и не требуется ссылка на весь объект IObserver, я думаю, что делегаты будут чище.
Во втором случае вместо IObervers, например, используются проходные делегаты.
public delegate void UpdateHandler(string message);
private UpdateHandler Update;
public void Register(UpdateHandler observerRoutine)
{
Update+=observerRoutine;
}
public void Unregister(UpdateHandler observerRoutine)
{
Update-=observerRoutine;
}
public void Notify(string message)
{
Update(message);
}
При этом наблюдателям не нужно реализовывать интерфейс. Вы даже можете передать лямбда-выражение. Вот в чем разница в уровне контроля. Хорошо это или плохо - решать вам.
Технически, вам не нужно использовать делегаты (кроме случаев использования обработчики событий, тогда это обязательно). Вы можете обойтись без них. На самом деле, это просто еще один инструмент в ящике для инструментов.
Первое, что приходит на ум при их использовании, - это Инверсия контроля. Каждый раз, когда вы хотите контролировать поведение функции извне, самый простой способ сделать это - поместить делегат в качестве параметра и заставить его выполнять делегат.
Я использовал вариации паттерна стратегии, которые довольно хорошо работали с делегатами стратегии. Или модель издатель / подписчик, где я предоставляю класс подписчика, который реализует 90 +% типичных функций подписчика в качестве оболочки для обратного вызова для обработки остальных 10%.
Вы официант, а я заявка. Я хочу сказать вам, чтобы вы вызывали один из моих методов, когда обнаруживаете, что что-то происходит. Для этого я передаю вам делегата моего метода, который я хочу, чтобы вы вызвали. Я сам не называю этот метод, потому что хочу, чтобы вы вызывали его, когда вы что-то обнаруживаете. Вы не вызываете мой метод напрямую, потому что вы не знаете (во время компиляции), что метод существует (меня даже не писали, когда вы были созданы); вместо этого вы вызываете любой метод, указанный делегатом, который вы получаете во время выполнения.
да, теперь я вас немного понимаю, но не могли бы вы предоставить какой-нибудь пример кода, который будет лучше для меня, чтобы понять
@ Cloud2010 Например, просмотрите все места, где используются делегаты и / или события в структуре> NET: например, Событие Control.Click или List <T> .ForEach Метод.
@ Cloud2010: Вот пример функции atexit (и ссылка на on_exit), которая выполняется при выходе из процесса. Функция bye (в примере) вызывается ОС, когда она обнаруживает, что процесс завершается. gnu.org/s/libc/manual/html_node/Cleanups-on-Exit.html
Я повторяю ответ, который дал этот вопрос.
Мне всегда нравилась метафора радиостанции.
Когда радиостанция хочет что-то передать, она просто отправляет это. Не нужно знать, действительно ли кто-нибудь там слушает. Ваше радио может регистрироваться на радиостанции (настраиваясь с помощью шкалы), и все радиопередачи (события в нашей маленькой метафоре) принимаются радио, которое переводит их в звук.
Без этого механизма регистрации (или события). Радиостанция должна будет связаться со всеми радиостанциями по очереди и спросить, хочет ли она трансляцию, если ваше радио ответит «да», а затем послать на нее сигнал напрямую.
Ваш код может следовать очень похожей парадигме, когда один класс выполняет действие, но этот класс может не знать или может не знать, кто будет заботиться о происходящем действии или действовать в соответствии с ним. Таким образом, он предоставляет возможность любому объекту зарегистрироваться или отменить регистрацию для уведомления о том, что действие имело место.
Это скорее аналогия с События, а не с делегаты в целом.
Думаю, я делаю прыжок или два, но делегат - это просто ссылка на метод. OP спросил, зачем вам нужна ссылка на метод. Я ответил метафорой классического использования делегатов (т.е. событий).
Когда вы можете напрямую вызвать метод, вам не нужен делегат.
Делегат полезен, когда код, вызывающий метод, не знает / не заботится о том, какой метод он вызывает является - например, вы можете вызвать длительную задачу и передать ей делегат методу обратного вызова, который задача может использовать для отправлять уведомления о своем статусе.
Вот (очень глупый) пример кода:
enum TaskStatus
{
Started,
StillProcessing,
Finished
}
delegate void CallbackDelegate(Task t, TaskStatus status);
class Task
{
public void Start(CallbackDelegate callback)
{
callback(this, TaskStatus.Started);
// calculate PI to 1 billion digits
for (...)
{
callback(this, TaskStatus.StillProcessing);
}
callback(this, TaskStatus.Finished);
}
}
class Program
{
static void Main(string[] args)
{
Task t = new Task();
t.Start(new CallbackDelegate(MyCallbackMethod));
}
static void MyCallbackMethod(Task t, TaskStatus status)
{
Console.WriteLine("The task status is {0}", status);
}
}
Как видите, класс Task не знает и не заботится о том, что - в данном случае - делегат относится к методу, который выводит статус задачи на консоль. Этот метод также может отправлять статус по сетевому соединению на другой компьютер. И т.п.
Делегаты - это строгая типизация интерфейсов функций / методов.
Если ваш язык придерживается позиции, что должна быть строгая типизация и что у него есть функции первого класса (обе из которых есть в C#), то это будет несовместимо с тем, что нет имеет делегатов.
Рассмотрим любой метод, который принимает делегат. Если бы у вас не было делегата, как бы вы ему что-то передали? И как у вызываемого могут быть какие-либо гарантии относительно его типа?
Вы не думаете как программист.
Вопрос в том, зачем вам вызывать функцию напрямую когда вы могли вызвать делегату?
A famous aphorism of David Wheeler goes: All problems in computer science can be solved by another level of indirection.
Я немного иронизирую. Очевидно, что большую часть времени вы будете вызывать функции напрямую, особенно внутри модуля. Но делегаты полезны, когда функция должна быть вызвана в контексте, где содержащий объект недоступен (или уместен), например обратные вызовы событий.
На самом деле между Sun и Microsoft произошел интересный спор по поводу делегатов. Хотя Sun заняла довольно жесткую позицию против делегатов, я считаю, что Microsoft сделала еще более решительный аргумент в пользу использования делегатов. Вот сообщения:
http://java.sun.com/docs/white/delegates.html
http://msdn.microsoft.com/en-us/vjsharp/bb188664.aspx
Думаю, вы найдете это чтение интересным ...
Вау, это ссылки из учебников истории. Я нахожу большинство вопросов, поднятых с обеих сторон, довольно тривиальными, и просто из-за неприязни между двумя сторонами по поводу того, что Microsoft затем меняет язык. Это в основном сводится к тому, стоит ли сохранение одной строки кода (сигнатуры метода) добавления делегатов. На мой взгляд, нет веских причин для их размещения.
Я думаю, что это больше связано с синтетический сахар и способом организации вашего кода, хорошее использование было бы для обработки нескольких методов, связанных с общим контекстом, которые принадлежат объекту или статическому классу.
дело не в том, что вы вынуждены их использовать, вы можете программировать что-то с ними и без них, но, возможно, их использование или не может повлиять на то, насколько организованным, читаемым и почему не крутым будет код, возможно, вы испортите несколько строк в вашем коде.
Каждый приведенный здесь пример хорош, где вы можете реализовать их, как кто-то сказал, это просто еще одна функция на языке, с которым вы можете играть.
Привет
Вот кое-что, что я могу описать как причину использования делегата. Следующий код написан на C#. Следите за комментариями.
public delegate string TestDelegate();
protected void Page_Load(object sender, EventArgs e)
{
TestDelegate TD1 = new TestDelegate(DiaplayMethodD1);
TestDelegate TD2 = new TestDelegate(DiaplayMethodD2);
TD2 = TD1 + TD2; // Make TD2 as multi-cast delegate
lblDisplay.Text = TD1(); // invoke delegate
lblAnotherDisplay.Text = TD2();
// Note: Using a delegate allows the programmer to encapsulate a reference
// to a method inside a delegate object. Its like the function pointer
// in C or C++.
}
//the Signature has to be same.
public string DiaplayMethodD1()
{
//lblDisplay.Text = "Multi-Cast Delegate on EXECUTION"; // Enable on multi-cast
return "This is returned from the first method of delegate explanation";
}
// The Method can be static also
public static string DiaplayMethodD2()
{
return " Extra words from second method";
}
Наилучшие пожелания, Притом Нанди, Бангладеш
Вот пример, который может помочь.
Есть приложение, использующее большой набор данных. Необходима функция, позволяющая фильтровать данные. Можно указать 6 различных фильтров.
Непосредственная мысль состоит в том, чтобы создать 6 различных методов, каждый из которых будет возвращать отфильтрованные данные. Например
public Data FilterByAge (int age)
общедоступные данные FilterBySize (размер int)
.... и так далее.
Это нормально, но очень ограничено и создает мусор, потому что он закрыт для расширения.
Лучше всего использовать один метод Filter и передавать информацию о том, как данные должны быть отфильтрованы. Здесь можно использовать делегата. Делегат - это функция, которую можно применить к данным для их фильтрации.
общедоступный фильтр данных (фильтр действий)
тогда код для использования становится
Фильтр (data => data.age> 30);
Фильтр (data => data.size = 19);
Данные кода => бла-бла становится делегатом. Код становится более гибким и остается открытым.
Вы спрашиваете об обосновании функционального программирования или зачем нужен тип «делегат» в C# для функционального программирования?