当一个事件有多个订阅者时,如何获得每个订阅者的返回值?

时间:2009-08-06 04:38:34

标签: c# events return-value subscription

代码如下所示:

时钟:

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声明为事件返回类型而不是手动声明委托是不错的做法?

2 个答案:

答案 0 :(得分:27)

使用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>,订阅者也可以通过获取和设置此属性来在有限的范围内协调他们的响应。

答案 1 :(得分:1)

我认为使用Action / Func代替委托是完全没问题的。

但事件不应该像这样使用。 它们是在无限期的时间点触发的,所以你只是不知道所有的参数。

你真正需要的可能是:

  1. 对时钟使用多态性。
  2. 使用visitor / subscriber / observer模式获取其值。
  3. 所以代码看起来像:

    var theClock = new Clock();
    theClock.AddSecondsSubscriber(new DisplayClock());
    theClock.AddSecondsSubscriber(new LogClock());
    theClock.RunAndExecuteVisitors( theBoolResultYouNeed => Console.WriteLine(theBoolResultYouNeed) );