我想知道我写的代码是否是一个正确编写的代码,它确实起作用,但我之前做过不好的设计,所以我需要知道我是否以正确的方式思考这个问题。
代码是关于使用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
}
}
}
简短说明:
我的问题是:
答案 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表达式,它定义了一个带有两个参数s
和e
(发送者和事件)的委托。当这被添加到_timer.Elapsed
事件(类型ElapsedEventHandler
)时,编译器可以从事件类型s
推断出类型e
和ElapsedEventHandler
。我们的委托执行的唯一操作是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)
我不知道你想要实现什么,但这里有一点关于你当前的设计:
您的GetTimer
在多线程模式下被破坏:
if (_timer == null)
_timer = new UpdaterTimer();
假设你有两个线程,每个线程同时调用GetTimer
,第一个检查_timer并发现它为null,所以它继续。但是在那时和它到达_timer = new UpdateTimer()
之前,线程上下文切换切换到另一个线程并暂停当前线程执行。所以另一个线程检查_timer
并发现它不为null所以它继续并创建一个新的计时器,现在上下文切换重新安排了第一个线程并继续执行,因此创建一个新计时器并更新旧计时器。正确使用单例模式使用静态构造函数而不是static UpdaterTimer() { _timer = new UpdaterTimer();}
开发人员可以根据需要调用GetTimer()
,因此会再次调用_timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
注册另一个Elapsed
处理程序。另请注意,无论何时拨打GetTimer
,计时器都会启动“_timer.Start()
”即使已停止。
请勿返回基础计时器,而是公开公开方法Start()
,Stop()
,UpdateInterval(int interval)
。
在SetTimer()
,您想立即致电DoStuff
,但会在等待SetTimer
的{{1}}方法中阻止,然后更好的方法是在新线程DoStuff()
中启动该方法或使用System.Threading.Timer而不是ThreadPool.QueueUserWorkItem(new WaitCallback((_) => DoStuff()));
并设置它立即调用方法start。