正确使用Timer类

时间:2011-07-18 20:06:41

标签: c# coding-style 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()必须运行,然后它必须在每个Timer.Elapsed事件上再次运行。

我的问题是:

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

3 个答案:

答案 0 :(得分:4)

我敢说,创建Timer的子类实用性很小。你已经将大约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();

其中_timer是包含类的属性。这里的意图很清楚。我不认为它应该属于自己的一类。

编辑:

(s,e)=>doStuff()

是一个lambda表达式,它定义了一个带有两个参数se(发送者和事件)的委托。当这被添加到_timer.Elapsed事件(类型ElapsedEventHandler)时,编译器可以从事件类型s推断出类型eElapsedEventHandler。我们的委托执行的唯一操作是doStuff()

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

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

lambda允许我们更简洁地写上面的内容。

答案 1 :(得分:2)

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

根据评论中OP的要求进行代码示例
注意:匹配规范(它是一个完全不同的用例,对于你的用例,你毕竟不需要一个单独的类(参考另一个答案))但是展示了如何使用组合和隐藏实施细节。 (此示例还有一些线程安全问题,但这不是重点。)

public static class TimerHelper
{
    private static Timer _timer;

    static TimerHelper()
    {
        _timer = new Timer(){AutoReset=true, Interval=1000};
        _timer.Start();
    }

    public static event ElapsedEventHandler Elapsed
    {
        add { _timer.Elapsed += value; } 
        remove { _timer.Elapsed -= value; }
    }
}

使用:

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

答案 2 :(得分:2)

我不知道你想要实现什么,但这里有一点关于你当前的设计:

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

    if (_timer == null) _timer = new UpdaterTimer();

    假设你有两个线程,每个线程同时调用GetTimer,第一个检查_timer并发现它为null,所以它继续。但是在那时和它到达_timer = new UpdateTimer()之前,线程上下文切换切换到另一个线程并暂停当前线程执行。所以另一个线程检查_timer并发现它不为null所以它继续并创建一个新的计时器,现在上下文切换重新安排了第一个线程并继续执行,因此创建一个新计时器并更新旧计时器。正确使用单例模式使用静态构造函数而不是static UpdaterTimer() { _timer = new UpdaterTimer();}

  2. 开发人员可以根据需要调用GetTimer(),因此会再次调用_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);注册另一个Elapsed处理程序。另请注意,无论何时拨打GetTimer,计时器都会启动“_timer.Start()”即使已停止。

  3. 请勿返回基础计时器,而是公开公开方法Start()Stop()UpdateInterval(int interval)

  4. SetTimer(),您想立即致电DoStuff,但会在等待SetTimer的{​​{1}}方法中阻止,然后更好的方法是在新线程DoStuff()中启动该方法或使用System.Threading.Timer而不是ThreadPool.QueueUserWorkItem(new WaitCallback((_) => DoStuff()));并设置它立即调用方法start。