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

Jef*_*eff 16 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));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

DisplayClock:

public class DisplayClock
{
    public static bool TimeHasChanged(DateTime now)
    {
        Console.WriteLine(now.ToShortTimeString() + " Display");
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

LogClock:

public class LogClock
{
    public static bool WriteLogEntry(DateTime now)
    {
        Console.WriteLine(now.ToShortTimeString() + " Log");
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

要运行代码:

var theClock = new Clock();
theClock.SecondChange += DisplayClock.TimeHasChanged;
theClock.SecondChange += LogClock.WriteLogEntry;
theClock.Run();
Run Code Online (Sandbox Code Playgroud)

其他问题是:

  • 每个订阅者返回一个值是不错的做法?
  • 将Action/Func声明为事件返回类型而不是手动声明委托是一种好习惯吗?

Jas*_*aty 28

使用Delegate.GetInvocationList.

if (SecondChange != null)
{
    DateTime now = DateTime.Now;
    foreach (Delegate d in SecondChange.GetInvocationList())
    {
        Console.WriteLine(d.DynamicInvoke(now));
    }
}
Run Code Online (Sandbox Code Playgroud)

使用Action/Func而不是手动声明委托是一种好习惯吗?

是.但我要指出,最佳做法是使用事件EventHandler<T>代替Func<..., TResult>.EventHandler<T>不支持返回值,但您有点合理,因为有一些.NET事件具有返回值.我认为在自定义EventArgs子类中使用可设置属性更好T.这就是我们在类似的事情中看到的模式KeyEventArgs.Handled.通过这种方式,您可以使用EventHandler<T>,订阅者也可以通过获取和设置此属性来在有限的范围内协调其响应.

  • 发送者 args 模式的另一个优点是,如果您添加其他数据,您只需向 arg 类添加一个元素即可提供额外数据,而不会破坏现有代码。如果您声明自己的委托接受单独的参数,则任何数据添加都会破坏您的所有客户端。这甚至不需要创建一个新的子类。 (2认同)