C# 定时器封装版

news2024/9/20 20:30:58

一、概述

在 Winform 等平台开发中,经常会用到定时器的功能,但项目定时器一旦写多了,容易使软件变卡,而且运行时间长了会造成软件的闪退,这个可能是内存溢出造成的,具体原因我也没去深究,另一个,就是在关闭软件时,经常关不掉,因为这时候定时器的线程依然还在运行,你就要把这些定时器一个个关闭,才能关闭软件,或者直接使用强制退出程序代码,非常的麻烦,后面我在想能不能封装一个定时器,使用事件的订阅机制来实现功能,后面就实现了这些功能,那么下面就开始搬代码吧。

二、实现功能

新建一个 winform 项目,添加一个类 ScanTimer.cs  。

代码:

using System;
using System.Threading;

/// <summary>
/// 定时器
/// </summary>
public class ScanTimer
{
    /// <summary>
    /// 定时器回调事件
    /// </summary>
    public static event Action ScanEvent;
    /// <summary>
    /// 定时器开关的状态
    /// </summary>
    public static Action<bool> TimerStatus = null;
    /// <summary>
    /// 定时器执行的次数
    /// </summary>
    public static Action<int> TimerExecuteCount = null;

    /// <summary>
    /// 定时器是否打开
    /// </summary>
    public static bool IsOpen
    {
        get
        {
            if (Timer == null)
                return false;
            return Timer.Enabled;
        }
    }

    //定时器
    private static System.Timers.Timer Timer = null;
    //间隔时间
    private const int IntervalTime = 2000;
    //定时清理控制台日志
    private static int OutCount = 0;
    //是否初始化
    private static bool IsInit = false;


    /// <summary>
    /// 初始化
    /// </summary>
    private static void Init()
    {
        //实例化Timer类,
        Timer = new System.Timers.Timer();
        //设置间隔时间(毫秒);
        Timer.Interval = IntervalTime;
        //到达时间的时候执行事件;
        Timer.Elapsed += new System.Timers.ElapsedEventHandler(Elapsed);
        //设置是执行一次(false)还是一直执行(true);
        Timer.AutoReset = true;

        IsInit = true;
    }

    /// <summary>
    /// 定时器
    /// </summary>
    /// <param name="source"></param>
    /// <param name="e"></param>
    private static void Elapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        OutCount++;
        if (OutCount > 10000)
        {
            OutCount = 0;
            //Console.Clear();
        }

        //定时器的执行次数
        if (TimerExecuteCount != null)
            TimerExecuteCount(OutCount);

        //执行回调
        if (ScanEvent != null)
            ScanEvent();
    }

    /// <summary>
    /// 打开定时器
    /// </summary>
    public static void Start()
    {
        if (!IsInit) Init();

        Timer.Enabled = true;
        OutCount = 0;

        if (TimerStatus != null)
            TimerStatus(true);
    }

    /// <summary>
    /// 关闭定时器
    /// </summary>
    public static void Stop()
    {
        Timer.Enabled = false;
        OutCount = 0;

        if (TimerStatus != null)
            TimerStatus(false);
    }

    /// <summary>
    /// 清除所有的事件
    /// </summary>
    public static void ClearAllEvent()
    {
        if (ScanEvent == null) return;
        Delegate[] dels = ScanEvent.GetInvocationList();
        foreach (Delegate del in dels)
        {
            ScanEvent -= del as Action;
        }
        Console.WriteLine("[ClearAllEvent]清除定时器所有的事件");
    }

    /// <summary>
    /// 获取定时器任务的个数
    /// </summary>
    /// <returns></returns>
    public static int GetTaskCount()
    {
        if (ScanEvent == null) return 0;
        Delegate[] dels = ScanEvent.GetInvocationList();
        return dels.Length;
    }

    /// <summary>
    /// 是否存在某个任务
    /// </summary>
    /// <param name="taskName"></param>
    /// <returns></returns>
    public static bool IsExistTask(string taskName)
    {
        if (ScanEvent == null)
            return false;

        Delegate[] dels = ScanEvent.GetInvocationList();
        foreach (Delegate del in dels)
        {
            object delObj = del.GetType().GetProperty("Method").GetValue(del, null);
            string funcName = (string)delObj.GetType().GetProperty("Name").GetValue(delObj, null);
            if (funcName == taskName) return true;
        }

        return false;
    }


    private ScanTimer() { }
}

给 Form1 窗体添加 Form1_Load,Form1_FormClosing 事件。

Form1_Load 事件是窗体启动时执行一次,在这里可以启动定时器。

Form1_FormClosing 事件是在窗体关闭时执行一次,在这里我们可以关闭定时器。

如下图:

 

三、测试

ScanTimer 的 ScanEvent 字段是一个事件,这里就是我们需要添加订阅机制的地方。

public static event Action ScanEvent;

其实原理也很简单,就是在 ScanTimer 类中封装一个定时器,每隔多少秒去执行一次这个事件,然后所有订阅这个事件的方法,都会被执行到。

在调用 ScanTimer 类之前,先把 winform 的输出类型改为 控制台应用程序,这样更方便的观察。

代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 定时器
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ScanTimer.ScanEvent += Timer;

            ScanTimer.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            ScanTimer.Stop();
        }

        private void Timer()
        {
            Console.WriteLine("定时器");
        }
    }
}

调用也非常的简单,只需要写一个 Timer() 方法,然后添加到 ScanEvent 这个事件中就好了,我们运行看看效果

另外,在 ScanTimer 类中我还封装了其他的方法,比如,你不知道定时器是否还在执行中,有时候执行代码,不一定需要在代码中打印, 可以使用 TimerExecuteCount 这个委托查看定时器执行了多少次,也可以通过 TimerStatus 这个委托,来查看定时器是否在执行。

代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace 定时器
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ScanTimer.ScanEvent += Timer;
            ScanTimer.TimerStatus += TimerStatus;
            ScanTimer.TimerExecuteCount += TimerExecuteCount;

            ScanTimer.Start();
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            ScanTimer.Stop();
        }

        private void Timer()
        {
            Console.WriteLine("定时器");
        }

        private void TimerStatus(bool sw)
        {
            Console.WriteLine("定时器的状态:{0}", sw);
        }

        private void TimerExecuteCount(int count)
        {
            Console.WriteLine("定时器执行次数:{0}", count);
        }
    }
}

运行:

在这里,TimerExecuteCount 和 TimerStatus 这两个委托是 Action 类型,如果其他的类不需要订阅这两个委托,不用使用 += ,可以直接使用 = 号就行。

ScanTimer.ScanEvent += Timer;
ScanTimer.TimerStatus = TimerStatus;
ScanTimer.TimerExecuteCount = TimerExecuteCount;

 

结束

如果这个帖子对你有所帮助,欢迎 关注 、点赞 、留言

end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/802664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年五大团队任务管理跟踪软件排行榜揭晓

企业对高效生产力的需求让团队任务管理跟踪软件在现代商业环境中变得越来越重要&#xff0c;然而市场上存在众多的任务管理工具&#xff0c;选择合适的工具对于提高工作效率和实现组织目标至关重要。本文章将提供一个客观的团队任务管理跟踪软件排行榜&#xff0c;帮助读者根据…

springboot创建并配置环境(二) - 配置基础环境

文章目录 一、介绍二、配置系统属性和环境变量三、配置自定义属性命令行参数四、作为应用配置信息 一、介绍 在上一篇文章&#xff1a;springboot创建并配置环境(一) - 创建环境中我们探讨了springboot是如何根据当前应用程序类型去创建对应的环境实例的。接下来探讨如何去配置…

java上传和解压任意压缩包

java上传zip文件并解压读取_java上传压缩包解压_梓隽的博客-CSDN博客 添加依赖 <dependency><groupId>com.github.junrar</groupId><artifactId>junrar</artifactId><version>7.4.1</version></dependency><dependency&g…

无涯教程-jQuery - hide( speed, callback)方法函数

hide(speed&#xff0c;[callback])方法使用优美的动画隐藏所有匹配的元素&#xff0c;并在完成后触发可选的回调。 hide( speed, [callback] ) - 语法 selector.hide( speed, [callback] ); 这是此方法使用的所有参数的描述- speed - 代表三个预定义速度("slow…

四. 点云着色(真彩点云)方案

前面内容&#xff1a; 一. 器件选型心得&#xff08;系统设计&#xff09;--1_goldqiu的博客-CSDN博客 一. 器件选型心得&#xff08;系统设计&#xff09;--2_goldqiu的博客-CSDN博客 二. 多传感器时间同步方案&#xff08;时序闭环&#xff09;--1 三. 多传感器标定方案&…

【业务功能篇57】Springboot + Spring Security 权限管理 【上篇】

4.权限管理模块开发 4.1 权限管理概述 4.1.1 权限管理的意义 后台管理系统中&#xff0c;通常需要控制不同的登录用户可以操作的内容。权限管理用于管理系统资源&#xff0c;分配用户菜单、资源权限&#xff0c;以及验证用户是否有访问资源权限。 4.1.2 RBAC权限设计模型 …

【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal

【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal 第六章 小程序事件绑定、动态提示Toast、对话框 Modal 文章目录 【微信小程序创作之路】- 小程序事件绑定、动态提示Toast、对话框 Modal前言一、事件是什么&#xff1f;二、小程序中常用事件三、事件传…

iOS--多线程原理

进程、线程与队列 进程的定义 进程是指在系统中正在运行的一个应用程序&#xff0c;如微信、支付宝app都是一个进程每个进程之间是独立的&#xff0c;每个进程均运行在齐专用的且受保护的内存 线程的定义 线程是进程的基本执行单元&#xff0c;一个进程的所有任务都在线程中…

【牛客网】链表的回文结构

思路1&#xff1a;找到中间节点&#xff0c;从中间节点向后反转链表 寻找链表的中间节点和反转链表的程序已经讲解过 寻找链表的中间节点&#xff1a;CSDN 反转链表&#xff1a;CSDN 然后从头节点和中间节点依次向后比较节点的值 &#x1f4d6;Note: 后半段链表反转后&#xff…

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

一、新系统上线如何规划容量&#xff1f; 1.套路总结 任何新的业务系统在上线以前都需要去估算服务器配置和JVM的内存参数&#xff0c;这个容量与资源规划并不仅仅是系统架构师的随意估算的&#xff0c;需要根据系统所在业务场景去估算&#xff0c;推断出来一个系统运行模型&…

事后多重比较方法

一、案例介绍 由单因素方差分析案例中&#xff0c;为研究郁金对低张性缺氧小鼠存活时间的影响&#xff0c;将36只小鼠随机生成A、B以及 C 三组&#xff0c;每组12个&#xff0c;雌雄各半&#xff0c;分别以10g/kg、20g/kg、40g/kg三种不同剂量的郁金灌胃&#xff0c;各组小鼠均…

vue elementui table去掉滚动条与实现表格自动滚动且无滚动条

当table内容列过多时&#xff0c;可通过height属性设置table高度以固定table高度、固定表头&#xff0c;使table内容可以滚动。 现在需求是右侧滚动条不好看&#xff0c;需要去除滚动条&#xff0c;并隐藏滚动条所占列的位置。让他可以滚动但是不出现滚动条,不然即时隐藏了滚动…

运动蓝牙耳机有什么推荐的?这几款非常值得入手!

运动不仅能够锻炼身体&#xff0c;还能调节情绪释放压力&#xff0c;每到周末均会去徒步或骑行&#xff0c;路途上少不了音乐的相伴&#xff0c;运动期间会佩戴耳机聆听音乐&#xff0c;边聆听喜欢的音乐或有声读物&#xff0c;边享受当下的美景。骨传导耳机可以说在多种耳机当…

每天一个电商API分享:获得淘宝商品快递费用 API

在电商行业中&#xff0c;快递费用是一项不容忽视的重要开支。随着电商行业的不断发展壮大&#xff0c;越来越多的消费者选择在线购物&#xff0c;这就增加了电商企业的订单量和物流压力。在这个过程中&#xff0c;快递费用起到了承载和运输商品的重要作用。 首先&#xff0c;…

easyUI框架学习

文章目录 一、前言二、引入使用easyUI 三、用法3.1 Dialog&#xff08;对话框窗口&#xff09;3.1.1 示例13.1.2 示例2 3.2 Layout&#xff08;布局&#xff09;3.2.1 示例1——通过标签创建布局3.2.2 示例2—— 创建嵌套布局 3.3 DateBox&#xff08;日期输入框&#xff09;3.…

【Spring框架】Bean作用域和生命周期

目录 Bean作用域设置Bean作用域生命周期⽣命周期演示 Bean作用域 Bean作用域指的是Bean在Spring容器中的某种行为(单例、原型…)。 Bean的6种作用域 singleton&#xff1a;单例作⽤域prototype&#xff1a;原型作⽤域&#xff08;多例作⽤域&#xff09;request&#xff1a;请…

Jenkins 配置maven和jdk

前提:服务器已经安装maven和jdk 一、在Jenkins中添加全局变量 系统管理–>系统配置–>全局属性–>环境变量 添加三个全局变量 JAVA_HOME、MAVEN_HOME、PATH 二、配置maven 系统管理–>全局工具配置–>maven–>新增 新增配置 三、配置JDK 在系统管…

node.js旅游景点分享网站【纯干货分享,免费领取源码03796】

node.js旅游景点分享网站 摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。旅游景点分享网站设计&#xff0c;主要的模块包括查看后台首页、轮播图&#xff08;轮播图管理&#xff09;、网站公告…

QSlider 样式 Qt15.15.2 圆形滑块

在看文档的时候测试了一下demo&#xff0c;然后发现了一个有意思的东西&#xff0c;自定义滑块为带边框的圆形。 在设置的时候边框总是和预期的有点误差&#xff0c;后来发现了这样一个计算方式可以画一个比较标准的圆。&#xff08;ABCDEF在下方代码块内&#xff09; 滑块的…

自定义信号槽机制

自定义信号槽机制 自定义信号自定义槽自定义信号和槽函数的使用解决办法 如果想要在QT类中自定义信号槽, 需要满足一些条件, 并且有些事项也需要注意: 要编写新的类并且让其继承Qt的某些标准类这个新的子类必须从QObject类或者是QObject子类进行派生在定义类的头文件中加入 Q_…