Когда событие имеет несколько подписчиков, как мне получить возвращаемое значение для каждого абонента?
Код выглядит следующим образом:
Часы:
public class Clock
{
public event Func<DateTime, bool> SecondChange;
public void Run()
{
for (var i = 0; i < 20; i++)
{
Thread.Sleep(1000);
if (SecondChange != null)
{
//how do I get return value for each subscriber?
Console.WriteLine(SecondChange(DateTime.Now));
}
}
}
}
DisplayClock:
public class DisplayClock
{
public static bool TimeHasChanged(DateTime now)
{
Console.WriteLine(now.ToShortTimeString() + " Display");
return true;
}
}
LogClock:
public class LogClock
{
public static bool WriteLogEntry(DateTime now)
{
Console.WriteLine(now.ToShortTimeString() + " Log");
return false;
}
}
Чтобы запустить код:
var theClock = new Clock();
theClock.SecondChange += DisplayClock.TimeHasChanged;
theClock.SecondChange += LogClock.WriteLogEntry;
theClock.Run();
Другие вопросы:
- Хорошая практика для каждого абонента вернуть значение?
- Можно ли просто объявить Action/Func как тип возвращаемого события вместо ручного объявления делегата?
Ответы
Ответ 1
Используйте Delegate.GetInvocationList
.
if (SecondChange != null)
{
DateTime now = DateTime.Now;
foreach (Delegate d in SecondChange.GetInvocationList())
{
Console.WriteLine(d.DynamicInvoke(now));
}
}
Хорошо ли использовать Action/Func вместо ручного объявления делегата?
Да. Но я укажу, что лучше всего использовать события EventHandler<T>
вместо Func<..., TResult>
. EventHandler<T>
не поддерживает возвращаемые значения, но вы несколько оправданы тем, что есть несколько событий .NET, которые имеют возвращаемые значения. Я считаю, что лучше иметь настраиваемое свойство в пользовательском подклассе EventArgs
, который вы используете в качестве T
. Это образец, который мы видим в таких вещах, как KeyEventArgs.Handled
. Таким образом, вы можете использовать EventHandler<T>
, и подписчики могут также скоординировать свои ответы в ограниченной степени, получая и устанавливая это свойство.
Ответ 2
Я думаю, что отлично использовать Action/Func вместо делегата.
НО События не должны использоваться так.
Они запускаются в неопределенный момент времени, поэтому вы просто не знаете все параметры.
Возможно, вам действительно нужно:
- Использовать полиморфизм для часов.
- Используйте шаблоны посетителя/подписчика/наблюдателя, чтобы получить их значения.
Итак, код будет выглядеть так:
var theClock = new Clock();
theClock.AddSecondsSubscriber(new DisplayClock());
theClock.AddSecondsSubscriber(new LogClock());
theClock.RunAndExecuteVisitors( theBoolResultYouNeed => Console.WriteLine(theBoolResultYouNeed) );