С# делегат против EventHandler
Я хочу отправить предупреждение всем подписчикам, когда произошла ловушка.
Код, который я создал, работает нормально, используя метод делегата myDelegate del
.
Мои вопросы:
-
Я хочу знать, лучше ли использовать EventHandler
вместо делегата? Я не уверен, каковы различия между делегатом и EventHandler
в моем случае.
-
notify(trapinfo t)
, что я сделал здесь, чтобы получить информацию о ловушке. Но, похоже, это не очень хорошая идея. Я прочитал некоторый онлайн-урок по введению объекта делегата; Мне интересно, уместно ли это в моем случае? И как мне это сделать? Какие-либо предложения?
Большое спасибо :)
Мой код:
public class trapinfo
{
public string info;
public string ip;
public string cause;
}
public class trap
{
public delegate void myDelegate(trapinfo t);
public myDelegate del;
trapinfo info = new trapinfo();
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
foreach (myDelegate d in del.GetInvocationList())
{
info.cause = "Shut Down";
info.ip = "192.168.0.1";
info.info = "Test";
d.Invoke(info);
}
}
}
}
public class machine
{
private int _occuredtime=0;
public trapinfo info = new trapinfo();
public void notify(trapinfo t)
{
++_occuredtime;
info.cause = t.cause;
info.info = t.info;
info.ip = t.ip;
getInfo();
}
public void subscribe(trap t)
{
t.del += new trap.myDelegate(notify);
}
public void getInfo()
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
info.cause, info.info, info.ip,_occuredtime);
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
machineA.subscribe(t);
t.run();
}
}
Обновление 2013-08-12
Как насчет наблюдателя/наблюдаемого шаблона проектирования, который отлично выглядит в моем случае (EventHandler
).
В моем случае машина подписывается на мессенджер. (Добавьте машину в список вызовов). Когда произошла ловушка, я отправляю сообщение всем машинам, на которые подписаны. (Вызов HandleEvent
чтобы справиться с этим)
Преимущества:
-
больше не заботится о GetInvocationList()
, просто используйте (+=)
и (-=)
чтобы решить, кому отправить ловушку.
-
Проще понять логику моей программы.
Я знаю, что есть несколько способов сделать это, но я хотел бы проанализировать его плюсы и минусы.
И спасибо за ваши комментарии и предложения, это было бы очень полезно!
Я прочитал статью MSDN EventArgs, которую предложил Мэтью Уотсон.
Вот моя версия события:
public class TrapInfoEventArgs : EventArgs
{
public int info { get; set; }
public string ip { get; set; }
public string cause { get; set; }
}
public class trap
{
public event EventHandler<TrapInfoEventArgs> TrapOccurred;
protected virtual void OnTrapOccurred(TrapInfoEventArgs e)
{
EventHandler<TrapInfoEventArgs> handler = TrapOccurred;
if (handler != null)
{
handler(this, e);
}
}
public void run()
{
//While(true)
// If a trap occurred, notify the subscriber
for (; ; )
{
Thread.Sleep(500);
TrapInfoEventArgs args = new TrapInfoEventArgs();
args.cause = "Shut Down";
OnTrapOccurred(args);
}
}
}
public class machine
{
public void c_TrapOccurred(object sender, TrapInfoEventArgs e)
{
Console.WriteLine("<Alert>: cauese/{0}, info/ {1}, ip/{2}, time/{3}",
e.cause, e.info, e.ip, DateTime.Now.ToString());
}
}
class Program
{
static void Main(string[] args)
{
trap t = new trap();
machine machineA = new machine();
t.TrapOccurred += machineA.c_TrapOccurred; //notify machine A
t.run();
}
}
Ответы
Ответ 1
Разница между событием и делегатом заключается в том, что:
Объявление события добавляет уровень защиты на экземпляр делегата. Эта защита препятствует тому, чтобы клиенты делегата сбрасывали делегата и его список вызовов, и позволяет только добавлять или удалять цели из списка вызовов.
Смотрите Каковы различия между делегатами и событиями?
2) На мой взгляд, ваш подписчик не должен свободно менять делегатов. Один подписчик может назначить =
вместо того, чтобы добавить +=
. Это назначит нового делегата, поэтому предыдущий делегат с его списком вызовов будет потерян, а предыдущие подписчики больше не будут вызываться. Так что вам стоит обязательно использовать Event. Или вы можете изменить свой код, чтобы сделать свой делегат закрытым, и написать дополнительные функции для управления им, чтобы определить собственное поведение события.
//preventing direct assignment
private myDelegate del ;
public void AddCallback(myDelegate m){
del += m;
}
public void RemoveCallback(myDelegate m){
del -= m;
}
//or
public static trap operator +(trap x,myDelegate m){
x.AddCallback(m);
return x;
}
public static trap operator -(trap x, myDelegate m)
{
x.RemoveCallback(m);
return x;
}
//usage
//t.AddCallback(new trap.myDelegate(notify));
t+=new trap.myDelegate(notify);
Ответ 2
Гораздо лучше использовать event
для вашего примера.
-
А event
понимается дизайнерами Visual Studio Form и WPF, поэтому вы можете использовать IDE для подписки на события.
-
При создании events
вам не нужно писать свою собственную обработку foreach
для итерации через них.
-
events
- это то, как большинство программистов ожидают, что эта функциональность будет доступна.
-
Если вы используете делегат, потребительский код может обходиться с ним способами, которые вы хотите предотвратить (например, сбросом своего списка вызовов). events
не позволяют этого делать.
Что касается второго вопроса: используя event
, вы должны создать класс, полученный из EventArgs
, для хранения данных и передать это событие, когда вы его поднимете. Затем потребитель получит доступ к нему.
Подробнее см. здесь: http://msdn.microsoft.com/en-us/library/system.eventargs.aspx