делегат с эвентом и без - чем отличаются?
Пишу типа того:
delegate void AAA();
а дальше одно из двух:
event AAA eeeAAA;
или просто (без event)
AAA eeeAAA;
в обоих случаях можно где-то дальше написать:
eeeAAA += acb_eeeAAA ;
Вопрос: зачем писать 'event' ?
1) с эвентом не работает оператор присваивания
2) эвент нельзя вызвать из вне
Я бы к этому добавил, что эти ограничения действуют вне класса и позволяют сохранить инкапсуляцию класса.
Инкапсулации и безопасности :)
Простой пример:
class ClassA { private ClassA () {} private static ClassA classA = null; public static ClassA Instance { get { if (classA != null) classA = new ClassA(); return classA; } } public delegate void AAA (); public AAA OnAAA; } class ClassB { public void Init () { ClassA.Instance.OnAAA += AAA_Fired; } private void AAA_Fired () { MakeAllPeopleHappy (); } } class ClassC { public void Init () { ClassA.Instance.OnAAA = AAA_Fired; } private void AAA_Fired () { MakeAllPeopleRich (); } }
И тут вожможны варианты :)
Если сделать так:
ClassB b = new ClassB (); ClassC c = new ClassC (); c.Init (); b.Init ();
то все люди буду и богатыми и счастливыми, а если сделать так:
ClassB b = new ClassB (); ClassC c = new ClassC (); b.Init (); c.Init ();
то все будут только богатыми :)
А ведь может еще случиться и так:
class ClassEvil { public void Init () { ClassA.Instance.OnAAA = AAA_Fired; ClassA.Instance.OnAAA (); } private void AAA_Fired () { MakeAllPeopleIll (); MakeAllPeoplePoor (); MakeAllPeopleUnhappy (); } }
Так что если ТС не хочет, чтобы ему вырвали руки, то пусть всегда использует event :D
Инкапсулации и безопасности :)
Полностью согласен. Ещё наверное назвал бы простоту Event механизма при использовании в других классах.
Еще вопросик. Нормально ли это использовать эвент для опроса подписчиков, а не для уведомления (как обычно)?
Т.е. использовать Функцию вместо Экшина, типа того:
class AAA
{
_____ public delegate bool IsX();
_____ public event IsX OnX;
_____ public bool IsAll()
_____ {
_____ _____ Delegate[] arr = OnX.GetInvocationList();
_____ _____ foreach (IsX x in arr)
_____ _____ _____ if (!x())
_____ _____ _____ _____ return false;
_____ _____ return true;
_____ }
_____ bool IsAny()
_____ {
_____ _____ Delegate[] arr = OnX.GetInvocationList();
_____ _____ foreach (IsX x in arr)
_____ _____ _____ if (x())
_____ _____ _____ _____ return true;
_____ _____ return false;
_____ }
}
ну со смыслом тут в порядке: это же типичная реализация обсерверов. Например, перед чем нибудь спросить всех обсерверов - а можно ли. И если все согласились - тогда делать.
На c++ не раз встречал подобное, только там надо было организовывать коллекции обсерверов.
Обсервер - простой наблюдатель, с чего бы ему что-то разрешать или запрещать?
Что хочет реализовать ТС тоже понятно... Но, как мне кажется, это обычная "протечка абстракции".
зависит от задачи чего могут обсерверы. И от задачи зависит какими полномочиями их наделять (точнее наделять базовый интерфейс-обсервер).
Еще вопросик. Нормально ли это использовать эвент для опроса подписчиков, а не для уведомления (как обычно)?
Нет. Event используют если нужно сообщить во внешний мир подписанным на этот Event классам что в определённом классе что что-то произошло (измение состояния, обрыв соединения и т.п.), при этом вызываются функции классов, которые на это сообщение как-то реагируют.
ну почему же нет? Даже Майкрософт в Windows.Forms использует евенты не только для уведомления, но и для опроса подписчиков. Пример тому напр. такие:
delegate void FormClosingEventHandler(object sender, FormClosingEventArgs e);
event FormClosingEventHandler FormClosing;
delegate void KeyEventHandler(object sender, KeyEventArgs e);
event KeyEventHandler KeyDown;
Просто здесь реакция подписчика возвращается через аргумент:
FormClosingEventArgs.Cancel
KeyEventHandler.Handled
Но в этом подходе есть однин недостаток: второй пописчик может перетереть решение предыдущего. А вот в предложенном мной выше решении (использовать Функцию вместо Экшина) этого недостатка нет. Правда, чуть больше кода писать надо. (Впрочем тоже самое можно написать и с Экшинами, а не функциями, но функции по-моему все же лучше)
Но в этом подходе есть однин недостаток: второй подписчик может перетереть решение предыдущего.
Уведомления с возвратом обычно реагируют на изменение состояния, а не на постоянную установку - это как бы правила хорошего тона. Так что это как бы теоретический недостаток реализации с гораздо большим количеством плюсов.
А вот потерять уведомление гораздо печальней и делать на каждый пук свой возврат тоже не очень хорошо.
Да и стандартную сигнатуру эвента без особой нужды лучше не менять. Ну и использование GetInvocationList() тоже как бы извращение.
А вот потерять уведомление гораздо печальнейконечно пример что я привел, можно переписать и так чтобы никто уведомлений не терял. Но надо ли?
Допустим на эвент FormClosing подписалось куча подписчиков, и если первый из них сказал Cancel=true, то форма, на мой взгляд, вполне может не дёргать остальных подписчиков, ввиду того что закрытие отменилось. А вот если таки станет дергать, то какой-то очередной подписчик скажет Cancel=false тем самым сделав проблемы первому. (Кстати именно так и происходит в Windows.Forms). А подписчики в идеале не должны ничего знать друг о друге.
Ну и использование GetInvocationList() тоже как бы извращение.ну тогда придется не пользоваться эвентами, а городить свою личную коллекцию делегатов.
Хотя можно конечно в каждом подписчике писать if (!e.Cancel)...
Даже Майкрософт в Windows.Forms использует евенты не только для уведомления, но и для опроса подписчиков.
Майкрософт не гнушается и goto использовать :D
Но в этом подходе есть однин недостаток: второй пописчик может перетереть решение предыдущего. А вот в предложенном мной выше решении (использовать Функцию вместо Экшина) этого недостатка нет.
Зависит от реализации :)
Можно же и по-человечески сделать ;) Например так:
public class MyCoolEvent : EventArgs { private bool isAllowed = true; public bool IsAllowed { get { return isAllowed; } set { isAllowed &= value; } } }
И никаких гвоздей :D
А вот если таки станет дергать, то какой-то очередной подписчик скажет Cancel=false тем самым сделав проблемы первому.
-----
Не совсем так,
Насколько Я помню, там в качестве параметра пасуется один и тот же объект.
Так что если кто-то в цепочке уже сказал Cancel=true, то всем остальным не нужно менять на Cancel=false.
Но это - детали имплементации подписчика и все зависит от задачи - возможно есть какая-то приоритизация подписчиков и старший говорит именно "закрыть в любом случае".
Хотя можно конечно в каждом подписчике писать if (!e.Cancel)...
-----
Угу...
если кто-то в цепочке уже сказал Cancel=true, то всем остальным не нужно менять на Cancel=false.совершенно верно, причем if (!e.Cancel) лучше написать таки.
Ведь FormClosingEventArgs.Cancel означает не отмену закрытия формы, а предписание игнорировать данный эвент: true if the event should be canceled; otherwise, false..
Хотя всё равно остается нелогичным поведение Формы: зачем дёргать подписчика с отменённым эвентом?!
зачем дёргать подписчика с отменённым эвентом?!
-----
Не знаю. Просто не думал.
У меня примерно такая же ситуация была. Очень комплексная форма, много-много динамических табов которые набиты контролами, определяемыми по содержимому версируемого объекта. И мне ее надо было закрывать... но только тогда когда все сохранено... а сохранением, в свою очередь, занимался другой код. Так что просто по кнопарику - не работает. Надо дергать что-то внешнее...
конечно пример что я привел, можно переписать и так чтобы никто уведомлений не терял. Но надо ли?
А надо ли менять "концепт" евентов? Есть относительно стандартная процедура их использования, к которой многие уже привыкли. Тем более для "генерации" события пишется одна строка, затем решарпер генерирует invocation function и затем делаем вызов. Хочется сделать одноразовый переключатель, меняем имплементацию EventArgs.
Один фиг нужно довольно часто иметь и дополнителные данные к событию. Так зачем в одном случает делать так, а в другом случае иначе?
ну тогда придется не пользоваться эвентами
По какой причине? Чисто по многократному изменению параметра? Так это издержки простоты реализации с "круглыми люками"