Deutsch
Germany.ruФорумы → Архив Досок→ Программирование

делегат с эвентом и без - чем отличаются?

412  1 2 все
anly коренной житель09.05.17 00:39
anly
NEW 09.05.17 00:39 

Пишу типа того:

delegate void AAA();

а дальше одно из двух:

event AAA eeeAAA;

или просто (без event)

AAA eeeAAA;


в обоих случаях можно где-то дальше написать:

eeeAAA += acb_eeeAAA ;


Вопрос: зачем писать 'event' ?

Проклят нарушающий межи ближнего своего (Втор.27:17)
#1 
Программист коренной житель09.05.17 08:56
09.05.17 08:56 
в ответ anly 09.05.17 00:39, Последний раз изменено 09.05.17 09:53 (Программист)

1) с эвентом не работает оператор присваивания

2) эвент нельзя вызвать из вне


#2 
Wanderer_ посетитель09.05.17 12:23
NEW 09.05.17 12:23 
в ответ Программист 09.05.17 08:56
1) с эвентом не работает оператор присваивания
2) эвент нельзя вызвать из вне


Я бы к этому добавил, что эти ограничения действуют вне класса и позволяют сохранить инкапсуляцию класса.

#3 
Программист коренной житель09.05.17 13:15
NEW 09.05.17 13:15 
в ответ Wanderer_ 09.05.17 12:23

Инкапсулации и безопасности :)


Простой пример:

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

#4 
Wanderer_ посетитель09.05.17 15:59
NEW 09.05.17 15:59 
в ответ Программист 09.05.17 13:15, Последний раз изменено 09.05.17 16:05 (Wanderer_)
Инкапсулации и безопасности :)

Полностью согласен. Ещё наверное назвал бы простоту Event механизма при использовании в других классах.

#5 
anly коренной житель09.05.17 16:07
anly
NEW 09.05.17 16:07 
в ответ Программист 09.05.17 08:56

Спасибо, понятно. Значит вещь полезная.

Проклят нарушающий межи ближнего своего (Втор.27:17)
#6 
anly коренной житель09.05.17 16:45
anly
NEW 09.05.17 16:45 
в ответ anly 09.05.17 00:39

Еще вопросик. Нормально ли это использовать эвент для опроса подписчиков, а не для уведомления (как обычно)?

Т.е. использовать Функцию вместо Экшина, типа того:


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;

_____ }

}

Проклят нарушающий межи ближнего своего (Втор.27:17)
#7 
Программист коренной житель09.05.17 17:20
NEW 09.05.17 17:20 
в ответ anly 09.05.17 16:45

Не вижу проблем... впрочем, как и смысла :)

#8 
anly коренной житель09.05.17 18:08
anly
NEW 09.05.17 18:08 
в ответ Программист 09.05.17 17:20

ну со смыслом тут в порядке: это же типичная реализация обсерверов. Например, перед чем нибудь спросить всех обсерверов - а можно ли. И если все согласились - тогда делать.

На c++ не раз встречал подобное, только там надо было организовывать коллекции обсерверов.

Проклят нарушающий межи ближнего своего (Втор.27:17)
#9 
Программист коренной житель09.05.17 19:58
NEW 09.05.17 19:58 
в ответ anly 09.05.17 18:08

Обсервер - простой наблюдатель, с чего бы ему что-то разрешать или запрещать?


Что хочет реализовать ТС тоже понятно... Но, как мне кажется, это обычная "протечка абстракции".

#10 
anly коренной житель09.05.17 20:13
anly
NEW 09.05.17 20:13 
в ответ Программист 09.05.17 19:58

зависит от задачи чего могут обсерверы. И от задачи зависит какими полномочиями их наделять (точнее наделять базовый интерфейс-обсервер).

Проклят нарушающий межи ближнего своего (Втор.27:17)
#11 
Wanderer_ посетитель09.05.17 21:25
NEW 09.05.17 21:25 
в ответ anly 09.05.17 16:45
Еще вопросик. Нормально ли это использовать эвент для опроса подписчиков, а не для уведомления (как обычно)?


Нет. Event используют если нужно сообщить во внешний мир подписанным на этот Event классам что в определённом классе что что-то произошло (измение состояния, обрыв соединения и т.п.), при этом вызываются функции классов, которые на это сообщение как-то реагируют.

#12 
anly коренной житель09.05.17 21:54
anly
NEW 09.05.17 21:54 
в ответ Wanderer_ 09.05.17 21:25, Последний раз изменено 09.05.17 21:58 (anly)

ну почему же нет? Даже Майкрософт в 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


Но в этом подходе есть однин недостаток: второй пописчик может перетереть решение предыдущего. А вот в предложенном мной выше решении (использовать Функцию вместо Экшина) этого недостатка нет. Правда, чуть больше кода писать надо. (Впрочем тоже самое можно написать и с Экшинами, а не функциями, но функции по-моему все же лучше)

Проклят нарушающий межи ближнего своего (Втор.27:17)
#13 
AlexNek патриот09.05.17 23:55
AlexNek
NEW 09.05.17 23:55 
в ответ anly 09.05.17 21:54
Но в этом подходе есть однин недостаток: второй подписчик может перетереть решение предыдущего.

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

А вот потерять уведомление гораздо печальней и делать на каждый пук свой возврат тоже не очень хорошо.

Да и стандартную сигнатуру эвента без особой нужды лучше не менять. Ну и использование GetInvocationList() тоже как бы извращение.

#14 
anly коренной житель10.05.17 08:38
anly
NEW 10.05.17 08:38 
в ответ AlexNek 09.05.17 23:55, Последний раз изменено 10.05.17 08:48 (anly)
А вот потерять уведомление гораздо печальней
конечно пример что я привел, можно переписать и так чтобы никто уведомлений не терял. Но надо ли?

Допустим на эвент FormClosing подписалось куча подписчиков, и если первый из них сказал Cancel=true, то форма, на мой взгляд, вполне может не дёргать остальных подписчиков, ввиду того что закрытие отменилось. А вот если таки станет дергать, то какой-то очередной подписчик скажет Cancel=false тем самым сделав проблемы первому. (Кстати именно так и происходит в Windows.Forms). А подписчики в идеале не должны ничего знать друг о друге.

Ну и использование GetInvocationList() тоже как бы извращение.
ну тогда придется не пользоваться эвентами, а городить свою личную коллекцию делегатов.

Хотя можно конечно в каждом подписчике писать if (!e.Cancel)...

Проклят нарушающий межи ближнего своего (Втор.27:17)
#15 
Программист коренной житель10.05.17 09:05
NEW 10.05.17 09:05 
в ответ anly 09.05.17 21:54
Даже Майкрософт в Windows.Forms использует евенты не только для уведомления, но и для опроса подписчиков.

Майкрософт не гнушается и goto использовать :D

#16 
Программист коренной житель10.05.17 09:11
NEW 10.05.17 09:11 
в ответ anly 09.05.17 21:54, Последний раз изменено 10.05.17 09:13 (Программист)
Но в этом подходе есть однин недостаток: второй пописчик может перетереть решение предыдущего. А вот в предложенном мной выше решении (использовать Функцию вместо Экшина) этого недостатка нет.

Зависит от реализации :)

Можно же и по-человечески сделать ;) Например так:

public class MyCoolEvent : EventArgs
{
    private bool isAllowed = true;
    public bool IsAllowed
    {
       get { return isAllowed; }
       set { isAllowed &= value; }
    }
}


И никаких гвоздей :D

#17 
anly коренной житель10.05.17 09:20
anly
NEW 10.05.17 09:20 
в ответ Программист 10.05.17 09:11

верно, хорошее решение

Проклят нарушающий межи ближнего своего (Втор.27:17)
#18 
Murr патриот10.05.17 11:42
Murr
NEW 10.05.17 11:42 
в ответ anly 10.05.17 08:38

А вот если таки станет дергать, то какой-то очередной подписчик скажет Cancel=false тем самым сделав проблемы первому.

-----

Не совсем так,

Насколько Я помню, там в качестве параметра пасуется один и тот же объект.

Так что если кто-то в цепочке уже сказал Cancel=true, то всем остальным не нужно менять на Cancel=false.

Но это - детали имплементации подписчика и все зависит от задачи - возможно есть какая-то приоритизация подписчиков и старший говорит именно "закрыть в любом случае".


Хотя можно конечно в каждом подписчике писать if (!e.Cancel)...

-----

Угу...

#19 
anly коренной житель10.05.17 16:38
anly
NEW 10.05.17 16:38 
в ответ Murr 10.05.17 11:42
если кто-то в цепочке уже сказал Cancel=true, то всем остальным не нужно менять на Cancel=false.
совершенно верно, причем if (!e.Cancel) лучше написать таки.

Ведь FormClosingEventArgs.Cancel означает не отмену закрытия формы, а предписание игнорировать данный эвент: true if the event should be canceled; otherwise, false..

Хотя всё равно остается нелогичным поведение Формы: зачем дёргать подписчика с отменённым эвентом?!

Проклят нарушающий межи ближнего своего (Втор.27:17)
#20 
1 2 все