Reputation: 3856
I'm interested creating an event handling object that you can subscribe for one time execution only and then the action is automatically unsubscribed
Is there similar native functionality in .NET? Here is what works for me right now:
public class CustomTimer
{
private event Action OneSecond;
private readonly Timer timer;
// Registered actions that should be called only once
private readonly ICollection<Action> oneOffs;
public CustomTimer()
{
this.timer = new Timer { Interval = 1000 };
this.timer.Elapsed += this.OnOneSecond;
this.oneOffs = new HashSet<Action>();
}
public bool IsRunning => this.timer.Enabled;
public void Start()
{
this.timer.Start();
}
public void Stop()
{
this.timer.Stop();
}
public void Subscribe(Action callback)
{
this.OneSecond += callback;
}
public void SubscribeOnce(Action callback)
{
this.oneOffs.Add(callback);
this.Subscribe(callback);
}
public void Unsubscribe(Action callback)
{
this.OneSecond -= callback;
this.oneOffs.Remove(callback);
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
this.OneSecond?.Invoke();
this.UnsubscribeOneOffs();
}
private void UnsubscribeOneOffs()
{
if (this.oneOffs.Count > 0)
{
foreach (var action in this.oneOffs)
{
this.OneSecond -= action;
}
this.oneOffs.Clear();
}
}
}
Here the events are set to execute every second.
How can I use similar strategy in other object that trigger events unpredictably and prevent events execution while the UnsubscribeOneOffs() method is running. Should I use some kind of lock?
Upvotes: 2
Views: 2912
Reputation: 6882
There is no need to register one time actions as OneSecond event handlers. Just keep them in a separate list.
public class CustomTimer
{
List<Action> _oneTimeActions = new List<Action>();
public void SubscribeOnce(Action handler)
{
lock(_oneTimeActions)
{
_oneTimeActions.Add(handler);
}
}
protected virtual void OnOneSecond(object sender, ElapsedEventArgs elapsedEventArgs)
{
// get a local copy of scheduled one time items
// removing them from the list.
Action[] oneTimers;
lock(_oneTimeActions)
{
oneTimers = _oneTimeActions.ToArray();
_oneTimeActions.Clear();
}
// Execute periodic events first
this.OneSecond?.Invoke();
// Now execute one time actions
foreach(var action in oneTimers)
{
action();
}
}
}
Upvotes: 1