I've made a timer class based on this discussion but I have a problem with it, the elapsed event occurs only one time. The smaller issue: It would be a good thing if the _timer not have to be instantiated at every calling of Start().
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Diagnostics.Contracts;
namespace JellystonePark.Model
{
internal delegate void TimerCallback(object state);
internal sealed class Timer : CancellationTokenSource, IDisposable
{
internal Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
{
Contract.Assert(period == TimeSpan.FromMilliseconds(-1), "This stub implementation only supports dueTime.");
Task.Delay(dueTime, Token).ContinueWith((t, s) =>
{
var tuple = (Tuple<TimerCallback, object>)s;
tuple.Item1(tuple.Item2);
}, Tuple.Create(callback, state), CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
public new void Dispose() { base.Cancel(); }
}
public class PCLTimer
{
private Timer _timer;
private TimeSpan _interval;
/// <summary>
/// Interval between signals in milliseconds.
/// </summary>
public double Interval
{
get { return _interval.TotalMilliseconds; }
set { _interval = TimeSpan.FromMilliseconds(value); Stop(); Start(); }
}
/// <summary>
/// True if PCLTimer is running, false if not.
/// </summary>
public bool Enabled
{
get { return null != _timer; }
set { if (value) Start(); else Stop(); }
}
/// <summary>
/// Occurs when the specified time has elapsed and the PCLTimer is enabled.
/// </summary>
public event EventHandler Elapsed;
/// <summary>
/// Starts the PCLTimer.
/// </summary>
public void Start()
{
if (0 == _interval.TotalMilliseconds)
throw new InvalidOperationException("Set Elapsed property before calling PCLTimer.Start().");
_timer = new Timer(OnElapsed, null, _interval, _interval);
}
/// <summary>
/// Stops the PCLTimer.
/// </summary>
public void Stop()
{
_timer.Dispose();
}
/// <summary>
/// Releases all resources.
/// </summary>
public void Dispose()
{
_timer.Dispose();
}
/// <summary>
/// Invokes Elapsed event.
/// </summary>
/// <param name="state"></param>
private void OnElapsed(object state)
{
if (null != _timer && null != Elapsed)
Elapsed(this, EventArgs.Empty);
}
}
}
If you interested in implementing Timer with Tasks, you can try this code:
public delegate void TimerCallback(object state);
public sealed class Timer : CancellationTokenSource, IDisposable
{
public Timer(TimerCallback callback, object state, int dueTime, int period)
{
Task.Delay(dueTime, Token).ContinueWith(async (t, s) =>
{
var tuple = (Tuple<TimerCallback, object>) s;
while (true)
{
if (IsCancellationRequested)
break;
Task.Run(() => tuple.Item1(tuple.Item2));
await Task.Delay(period);
}
}, Tuple.Create(callback, state), CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
public new void Dispose() { base.Cancel(); }
}
As already mentioned you can introduce dependency on some interface and ask user to give you particular timer implementation.