Recently I am rewriting an old App using WinRT, I need to display a clock, but I find there is no Timer control.
It looks like I have to implement the Timer myself. I don't use Thread.Sleep
because it will block UI thread. I prefer using async await over it. To replace Thread.Sleep
, I use Task.Delay
:
while (true)
{
// work
await Task.Delay(ms);
}
To increase reusability, we need to encapsulate further.
The first is the loop condition and the number of milliseconds, which can be controlled, so add attributes:
public int Interval { get; set; }
public bool IsEnabled { get; set; }
This allows you to:
while (IsEnabled)
{
// 要做的操作
await Task.Delay(Interval);
}
Further decoupling is to leave the operation part to the user to implement, the glorious C# language provides an Action delegate to extract this part of the code, so it is necessary to define the properties of the type of an Action delegate, and let the user decide the behavior by himself.
public Action<int> DoAction { get; set; }
Because the user may want to read the value of interval, the action parameter is int interval. Now you can do this:
while (IsEnabled) { DoAction(Interval); await Task.Delay(Interval); }
In recent years, the promise and futures programming styles have become popular. By returning this
, you can type "." all the way down when you write code. So the place to assign a value to a property can be designed like this:
public AsyncTimer SetInterval(int ms) { if (ms <= 0) { this.Interval = ms; } else { throw new ArgumentOutOfRangeException("ms", "interval must be larger than zero."); } return this; } public AsyncTimer WhenTick(Action<int> action) { this.DoAction = action; return this; }
既然用户要使用Timer肯定得有一个interval,所以要用带参构造去勾引用户完成interval的赋值,同时也允许不带参构造函数。
public AsyncTimer(int? ms = null) { if (null != ms) { if (ms <= 0) { throw new ArgumentOutOfRangeException("ms", "interval must be larger than zero."); } Interval = ms.Value; } }
最后增加一对start/stop方法,也可以再奖励一个toggle方法,就可以完成装逼了,最终封装成这样:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DateDiffer.WinRT { public class AsyncTimer { public int Interval { get; set; } public bool IsEnabled { get; set; } public Action<int> DoAction { get; set; } public AsyncTimer(int? ms = null) { if (null != ms) { if (ms <= 0) { throw new ArgumentOutOfRangeException("ms", "interval must be larger than zero."); } Interval = ms.Value; } } public AsyncTimer SetInterval(int ms) { if (ms <= 0) { this.Interval = ms; } else { throw new ArgumentOutOfRangeException("ms", "interval must be larger than zero."); } return this; } public void Stop() { IsEnabled = false; } public AsyncTimer WhenTick(Action<int> action) { this.DoAction = action; return this; } public async Task StartAsync() { if (Interval <= 0) { throw new InvalidOperationException("interval must be set."); } if (DoAction == null) { throw new InvalidOperationException("action must be set."); } IsEnabled = true; while (IsEnabled) { DoAction(Interval); await Task.Delay(Interval); } } public void Toggle() { IsEnabled = !IsEnabled; } } }
然后调用的地方就是这样:
TimerTask = new AsyncTimer(1000).WhenTick(i => OnOneSecondPassed()).StartAsync(); ... protected virtual void OnOneSecondPassed() { CurrentTime = DateTime.Now; }
我是一名坚定的果黑
windows软件开发者,好评,向你学习~
Komey
Why 同是Azure香港。。。。 打开速度怎么差了这么多·· 晕
Gee
这个有好多问题啊,先说 bug:比如连续 Start 两次会死,比如 Stop 之后 Toggle 并不会真的开始 Timer 的 Tick 功能,比如 SetInterval 中不等号反向。再说设计的不一致性:再比如一般要传入 sender 和 e。最后说设计的不好之处:完全可以用 public async void StartAsync 然后里面用 Thread.Sleep,这样不会阻塞调用线程,简单得多。
C
public AsyncTimer SetInterval(int ms)
这个方法非常不理解 ...
CraigTaylor
确实逼格很高
Shawn
你好!我算是初学者,有两个问题请教。首先泛型委托为什么要指定int参数?然后Gee说的"两次Start会死"以及"sender和e"何解?谢谢
嗯嗯
难怪代码没有高亮,原来是没有这个控件