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