C#如何重启一个计时器
一. 废话
今天在做项目的时候遇到了如何重启一个计时器的问题,C# 中有很多计时器,但是它们还真的没有一个用来 " Restart " 的方法。
二. 没用的分类
C# 系统中有好多种类的计时器:
- System.Timers.Timer
- System.Threading.Timer
- System. Windows.Threading.DispatcherTimer
- System.Windows.Forms.Timer
三. 强行增加篇幅贴代码
这边先使用 System.Timers.Timer 来做一下测试的代码演示。测试代码为:
//10秒触发一次计时间隔 const int Interval = 10 * 1000; //定义计时器 Timer timer = new Timer(Interval); //计时器间隔触发事件 timer.Elapsed += (o, e) => { Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }; timer.Start(); Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); //按下便是重置 Console.ReadKey(); Console.WriteLine("开始计时器重置"); timer.Stop(); timer.Interval = Interval; timer.Start(); Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey(); Console.WriteLine("开始计时器暂停恢复能否重置?"); timer.Stop(); timer.Start(); Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey(); Console.WriteLine("直接Start一次能否重置?"); timer.Start(); Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey();
运行结果:
可以看到,我们可以使用两种方式来对 Timer 进行重置:
1. 通过重新设置 Interval 的属性,然后再通过 Start 方法重新开启计时器(最后一种测试,只单纯使用 Start 方法开启计时器是没有效果的);
这边通过观察 Timer 的 源代码 156 行,可以看到 Interval 的内部会做一个刷新的操作,如下图:
2. 通过先 Stop 方法停止计时器,然后再使用 Start 方法进行重新开启;
!!那么问题来了,是不是对于所有不同程序集下的计时器都是这样的呢?!!
答:并不是的,不同计时器有点不同。下面是分别对 System.Threading.Timer 、System.Windows.Threading.DispatcherTimer 以及 System.Windows.Forms 的测试。
① 对 System.Threading.Timer 的测试:
对于这个计时器,有一些坑可以在这篇文章中进行学习《 C# 工作总结(三):System.Threading.Timer 的回收问题 》。它并没有 Start 和 Stop 方法,也没有 Interval 属性,在构造函数中通过设置一些初始值之后,就会开始启动(有一个起始触发一次的延迟,可以看到每次启动之前先会步过这个事件先触发一次)。
测试代码:
//10秒触发一次计时间隔 const int Interval = 10 * 1000; //定义计时器 Timer timer = new Timer((state) => { Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }, null, 0, Interval); Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); //按下便是重置 Console.ReadKey(); Console.WriteLine("开始计时器重置"); timer.Change(0, Interval); Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey();
运行结果:
可以看到我们只能通过使用 Change 的方法对这个计时器进行重置。
② 对 System.Windows.Threading.DispatcherTimer 的测试:
这个计时器是来自于 WPF 框架的计时器,它和 System.Timers.Timer 和 System.Threading.Timer 的区别在于,它是在 UI 线程中运行的,有点类似于 System.Windows.Forms.Timer 。在 UI 线程中使用的区别就是,对于 System.Windows.Threading.DispatcherTimer 和 System.Windows.Forms.Timer 在修改 UI 的时候,不需要使用 Invoke 或者是 BeginInvoke 来避免跨线程访问修改 UI 控件的问题。
经过测试发现,在 Console 中使用 System.Windows.Threading.DispatcherTimer 会失败(没有计时间隔触发,但也不抛出异常)。下面是一个测试:
新建一个窗体的 Loaded 加载事件,然后输入如下代码:
XAML 代码:
<Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="30"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/> </Grid.ColumnDefinitions> <ListBox x:Name="listBox" Grid.ColumnSpan="3" /> <Button x:Name="btn1" Grid.Row="1" Grid.Column="0" Content="Interval重置" Click="btn1_Click"/> <Button x:Name="btn2" Grid.Row="1" Grid.Column="1" Content="Stop与Start重置" Click="btn2_Click"/> <Button x:Name="btn3" Grid.Row="1" Grid.Column="2" Content="仅仅Start" Click="btn3_Click"/> </Grid>
后台代码:
/// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { //定义计时器 private DispatcherTimer timer = null; //10秒触发一次计时间隔 private const int Interval = 10; public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { //定义计时器 timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromSeconds(Interval); //计时器间隔触发事件 timer.Tick += (_o, _e) => { this.listBox.Items.Add("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }; timer.Start(); this.listBox.Items.Add("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private void btn1_Click(object sender, RoutedEventArgs e) { //按下便是重置; this.listBox.Items.Add("开始计时器重置"); timer.Stop(); timer.Interval = TimeSpan.FromSeconds(Interval); timer.Start(); this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private void btn2_Click(object sender, RoutedEventArgs e) { this.listBox.Items.Add("开始计时器暂停恢复能否重置?"); timer.Stop(); timer.Start(); this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private void btn3_Click(object sender, RoutedEventArgs e) { this.listBox.Items.Add("直接Start一次能否重置?"); timer.Start(); this.listBox.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } }
运行结果:
可以看出对于 System.Windows.Threading.DispatcherTimer 来说,它的重置方法和 System.Timers.Timer 一样。
③ 对 System.Windows.Forms.Timer 的测试:
System.Windows.Forms.Timer 与 System.Windows.Threading.DispatcherTimer 的特点非常的类似,也是不能在 Console 控制台中运行。下面是测试代码:
public partial class Form1 : Form { //定义计时器 private Timer timer = null; //10秒触发一次计时间隔 private const int Interval = 10 * 1000; public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { timer = new Timer(); timer.Interval = Interval; timer.Tick += (_o, _e) => { this.listBox1.Items.Add("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }; timer.Start(); } private void button1_Click(object sender, EventArgs e) { this.listBox1.Items.Add("开始计时器重置"); timer.Stop(); timer.Interval = Interval; timer.Start(); this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private void button2_Click(object sender, EventArgs e) { this.listBox1.Items.Add("开始计时器暂停恢复能否重置?"); timer.Stop(); timer.Start(); this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } private void button3_Click(object sender, EventArgs e) { this.listBox1.Items.Add("直接Start一次能否重置?"); timer.Start(); this.listBox1.Items.Add("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); } }
运行结果:
测试的结果和 System.Windows.Threading.DispatcherTimer 和 System.Timers.Timer 一致。
四. 没啥用的技巧
为使用方法,我们可以使用 扩展方法 对着几个 Timer 进行一下扩展,这边以 System.Timers.Timer 来做一下扩展。
代码如下:
/// <summary> /// 扩展方法类 /// </summary> public static class Extensions { /// <summary> /// 给 System.Timers.Timer 增加一个扩展方法 Restart 用于重新开始重置计时间隔 /// </summary> /// <param name="timer"></param> /// <param name="invertal">重置计时间隔的长度</param> public static void Restart(this System.Timers.Timer timer, int invertal = 0) { //重新开始 timer.Stop(); if (invertal > 0) { timer.Interval = invertal; } else { //set <- get timer.Interval = timer.Interval;//利用内部的change方法 } //重新开始 timer.Start(); } }
引入这个静态的扩展类之后,我们就可以上端使用这个扩展方法:
//10秒触发一次计时间隔 const int Interval = 10 * 1000; //定义计时器 Timer timer = new Timer(Interval); //计时器间隔触发事件 timer.Elapsed += (o, e) => { Console.WriteLine("计时器触发:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); }; timer.Start(); Console.WriteLine("开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey(); Console.WriteLine("开始计时器重置"); //调用 timer.Restart(); Console.WriteLine("重新开始开始计时器:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); Console.ReadKey();