2

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);
        }
    }
}
Community
  • 1
  • 1
Müller András
  • 191
  • 3
  • 12
  • I'm unclear of the actual question you're looking for an answer to? – Peter Ritchie Dec 07 '13 at 18:55
  • You'll need to specify the *period* to get the timer to fire more than once. You'll then discover why this code sucks. – Hans Passant Dec 07 '13 at 19:03
  • @HansPassant Have you any idea, how to define period? I'm not wanting a full working code, only some guidlines, how to start with it. I have to implement a timer in a pcl library, i haven't found any working solutions, if you better than this, please share with me. – Müller András Dec 07 '13 at 23:43
  • Whatever app uses your library won't have any trouble giving you timer objects. Write an ITimer and ITimerFactory interface. – Hans Passant Dec 07 '13 at 23:58

1 Answers1

3

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.

Ivan Leonenko
  • 2,363
  • 2
  • 27
  • 37