when an event has multiple subscribers, how do I get the return value for each subscriber?

Jeff picture Jeff · Aug 6, 2009 · Viewed 13.5k times · Source

The code looks like below:

Clock:

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;
    }
}

To run the code:

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

The other questions are:

  • Is it good practice for each subscriber to return a value?
  • Is it good practice to just declare Action/Func as the event return type instead of manually declaring a delegate?

Answer

Jason Kresowaty picture Jason Kresowaty · Aug 6, 2009

Use Delegate.GetInvocationList.

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

is it good practice to just use Action/Func instead of manually declaring a delegate?

Yes. But I will point out that the best practice is for events to use EventHandler<T> instead of Func<..., TResult>. EventHandler<T> does not support return values, but you are somewhat justified in that there are a few .NET events that have return values. I would consider it better to have a settable property in a custom EventArgs subclass that you use as your T. This is the pattern we see in things like KeyEventArgs.Handled. In this way, you can use EventHandler<T> and the subscribers can also coordinate their responses to a limited extent by getting and setting this property.