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)
{
// 要做的操作
await Task.Delay(毫秒);
}
为了增加逼格和可重用性,我们需要进一步封装。注意观察本高(diao)手(si)是如何一步步提高逼格的。
首先是循环条件和毫秒数,要能被控制,所以加入属性:
public int Interval { get; set; }
public bool IsEnabled { get; set; }
这样就可以:
while (IsEnabled)
{
// 要做的操作
await Task.Delay(Interval);
}
进一步解耦,就是把操作部分留给用户去实现,光荣伟大正确的C#语言提供了Action委托可以把这部分代码抽离出来,所以要定义一个Action委托的类型的属性,让用户自己去决定行为。
public Action<int> DoAction { get; set; }
因为用户有可能想要读取interval的值,所以Action传入参数为int类型的interval。现在就可以这样:
while (IsEnabled) { DoAction(Interval); await Task.Delay(Interval); }
你们以为逼格完整了吗?错,还欠缺很多。
首先是这几年流行的promise和futures编程风格。通过return 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的赋值,同时也允许不带参构造给用户强迫症的关爱。这时候菜鸟们就会写出2个构造函数来,其实装逼的办法是用C#的可选参数:
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软件开发者,好评,向你学习~
Why 同是Azure香港。。。。 打开速度怎么差了这么多·· 晕
这个有好多问题啊,先说 bug:比如连续 Start 两次会死,比如 Stop 之后 Toggle 并不会真的开始 Timer 的 Tick 功能,比如 SetInterval 中不等号反向。再说设计的不一致性:再比如一般要传入 sender 和 e。最后说设计的不好之处:完全可以用 public async void StartAsync 然后里面用 Thread.Sleep,这样不会阻塞调用线程,简单得多。
public AsyncTimer SetInterval(int ms)
这个方法非常不理解 ...
确实逼格很高
你好!我算是初学者,有两个问题请教。首先泛型委托为什么要指定int参数?然后Gee说的"两次Start会死"以及"sender和e"何解?谢谢
难怪代码没有高亮,原来是没有这个控件