C# 正确使用定时器类

C# 正确使用定时器类,c#,coding-style,timer,C#,Coding Style,Timer,我想知道我写的代码是否是正确编写的代码,它是否正常工作,但我以前做过不好的设计,所以我需要知道我是否以正确的方式思考这个问题 代码是关于使用System.Timers.Timer每X小时执行一次重复操作。我在stackoverflow上读了一些关于这个主题的帖子,然后我试着写我自己的类。以下是我写的: namespace MyTool { public class UpdaterTimer : Timer { private static UpdaterTimer _timer;

我想知道我写的代码是否是正确编写的代码,它是否正常工作,但我以前做过不好的设计,所以我需要知道我是否以正确的方式思考这个问题


代码是关于使用System.Timers.Timer每X小时执行一次重复操作。我在stackoverflow上读了一些关于这个主题的帖子,然后我试着写我自己的类。以下是我写的:

namespace MyTool
{
public  class UpdaterTimer : Timer
{
    private static UpdaterTimer _timer;
    protected UpdaterTimer()
    { }

    public static UpdaterTimer GetTimer()
    {
        if (_timer == null)
            _timer = new UpdaterTimer();

        SetTimer(_timer);
        return _timer;
    }

    private static void SetTimer(UpdaterTimer _timer)
    {
        _timer.AutoReset = true;
        _timer.Interval = Utils.TimeBetweenChecksInMiliseconds;
        _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
        _timer.Start();
        DoStuff();
    }

    static void _timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        DoStuff();
    }

    private static void DoStuff()
    {
       //does stuff on each elapsed event occurrence
    }
}
}
简要说明:

  • 尝试使用单例模式,因为我只需要一个计时器就可以工作
  • 我不确定是否在SetTimer()方法中调用DoStuff(),这似乎是多余的。但逻辑是,当应用程序启动时,DoStuff()必须运行,然后它必须在每个计时器上再次运行
我的问题是:

  • 根据规范,您是否会以不同的方式编写此行为
  • 在这种情况下使用单例是可以的,还是没有意义

  • 我认为你应该在这里使用合成而不是继承。(即,不从计时器继承,只将计时器用作专用字段)
    此外,若类之外的代码不应更改计时器的属性,则不应将其显示给其他类。如果您想让外部代码能够订阅计时器的
    appeated
    事件,您可以为它添加一个事件,并在
    DoStuff
    处理程序中触发它,如果没有,只需隐藏它即可

    根据OP在注释中的要求编写示例代码:
    注意:它与规范不匹配(这是一个完全不同的用例,对于您的用例,您毕竟不需要单独的类(请参阅另一个答案)),但显示了如何使用组合和隐藏实现细节。(该示例还存在一些线程安全问题,但这不是重点。)

    以及使用:

    // somewhere else in code
    TimerHelper.Elapsed += new ElapsedEventHandler(TimerHelper_Elapsed);
    

    我敢说,制作计时器的子类没有什么用处。您已经将大约7行代码转换成了一堆膨胀的代码。考虑到您的
    DoStuff
    方法是私有的,因此它不会以任何方式被重用。因此,考虑以下几行:

    Action doStuff=()=>{
        //does stuff on each elapsed event occurance
    };
    _timer=new System.Timers.Timer(){
        AutoReset = true,
        Interval = Utils.TimeBetweenChecksInMiliseconds
    };
    _timer.Elapsed += (s,e)=>doStuff();
    _timer.Start();
    doStuff();
    
    其中
    \u timer
    是包含类的属性。这里的意图是明确的。我认为它不配拥有自己的一个阶级

    编辑: 是一个lambda表达式,它定义了一个委托,该委托接受两个参数
    s
    e
    (发送方和事件)。当这被添加到
    \u timer.appeased
    事件(类型为
    ElapsedEventHandler
    )中时,编译器可以从事件类型
    ElapsedEventHandler
    推断类型
    s
    e
    。我们的代理执行的唯一操作是
    doStuff()

    从长远来看,这可以扩展到:

    _timer.Elapsed += delegate(object sender, ElapsedEventArgs e){doStuff();};
    

    lambda允许我们更简洁地编写上述内容。

    我不知道您想要实现什么,但以下是关于您当前设计的几点:

  • 您的
    GetTimer
    在多线程模式下被破坏:

    if(_timer==null)
    _计时器=新的UpdateTime()

    假设您有两个线程,每个线程同时调用
    GetTimer
    ,第一个线程检查\u计时器并发现它为空,因此它继续。但是,在到达
    \u timer=new UpdateTimer()
    之前,线程上下文切换切换到另一个线程并暂停当前线程的执行。因此,另一个线程检查
    \u timer
    并发现它不是空的,因此它继续并创建一个新的计时器,现在上下文开关重新调度第一个线程并继续执行,因此创建一个新的计时器并更新旧的计时器。要正确使用单例模式,请改用静态构造函数
    staticupdatetimer(){{u timer=newupdatetimer();}

  • 开发人员可以根据需要调用
    GetTimer()
    ,因此它将调用
    \u timer.appeased+=new ElapsedEventHandler(\u timer\u appeased)再次注册另一个
    已用
    处理程序。还要注意,无论何时调用
    GetTimer
    ,计时器都将启动“
    \u timer.start()
    ”,即使它已停止

  • 不要返回底层计时器,而是公开公共方法
    Start()
    Stop()
    UpdateInterval(int interval)

  • SetTimer()
    中,您希望立即调用
    DoStuff
    ,但这将在
    SetTimer
    方法中被阻止,等待
    DoStuff()
    完成,更好的方法是在新线程
    ThreadPool.QueueUserWorkItem(new WaitCallback((\u)=>DoStuff())中启动该方法
    或使用而不是
    System.Timers.Timer
    并将其设置为立即调用方法start


  • “每X小时重复一次”听起来像是一项计划任务…@Tocco谢谢你的评论,在这种情况下,它将从那里删除。@Andrei,呃。。。我错了,
    DoStuff()
    将不会在
    Start()
    上调用。对不起=/@Marc Gravell-我很担心你的评论,你的意思是计时器根本不应该用于预定的任务?你能解释一下原因吗@Tocco-否problem@Andrei不,我不是说“一点也不”;但是,在这个时间间隔内,某些事情可能更容易作为计划任务进行安排。你不必-我不是强迫你这么做的。我的建议只是为了确保您已经考虑了所有可用的选项忠实于原始设计。也许OP希望在等待第一次计时之前立即调用?非常感谢;由于这是我的第一个此类任务,有时间的行动,您的评论让我能够学习;但是我听不懂这句话,你能解释一下吗:_timer.appeased+=(s,e)=>doStuff()@安德烈:我为你添加了一些信息。谢谢你的评论;我继承了Timer,因此UpdateTimer对象将具有计时器的属性。我无法调用_timer.Start(),所以我想我应该继承timer。另外,你能帮我吗
    (s,e)=>doStuff()
    
    _timer.Elapsed += delegate(object sender, ElapsedEventArgs e){doStuff();};