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:
The class that raises the event is called the publisher and the classes that handle the event are called subscribers. An event can have multiple subscribers.
The standard signature of an event handler delegate defines a method that does not return a value. This method's first parameter is of type Object and refers to the instance that raises the event. Its second parameter is derived from type EventArgs and holds the event data.
To subscribe to events by using the Visual Studio IDEOn top of the Properties window, click the Events icon. Double-click the event that you want to create, for example the Load event. Visual C# creates an empty event handler method and adds it to your code. Alternatively you can add the code manually in Code view.
+= subscribes to an event. The delegate or method on the right-hand side of the += will be added to an internal list that the event keeps track of, and when the owning class fires that event, all the delegates in the list will be called.
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.
I think it is perfectly fine to use Action/Func instead of the delegate.
BUT Events are not supposed to be used like that. They are triggered at indefinite time point, so you just don't know all the parameters.
What you really need is probably:
So the code will look like:
var theClock = new Clock();
theClock.AddSecondsSubscriber(new DisplayClock());
theClock.AddSecondsSubscriber(new LogClock());
theClock.RunAndExecuteVisitors( theBoolResultYouNeed => Console.WriteLine(theBoolResultYouNeed) );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With