IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Windows Phone 8.1 (WinRT) 如何实现Timer

    汪宇杰发表于 2014-07-31 01:15:11
    love 0

    最近在用WinRT重写以前的一个应用,需要显示一个时钟,结果发现没有Timer控件。。。

    看来只能自己写代码实现了。屌丝的做法是Thread.Sleep,但那个会卡UI线程,用户会操作不了任何东西。正确的思路应该是用.NET 4.5的async await,不会卡UI线程。替代Thread.Sleep的方法是Task.Delay,它是awaitable的,所以代码是这样的:

    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 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 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 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 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;
    }

    非常的高大上。



沪ICP备19023445号-2号
友情链接