我喜欢罗伯·库克的回答,所以我建了一个小房子
EagerTimer
子类的类
System.Timers.Timer
并添加了此功能(有来自
these
articles
)
我知道我需要
System.Threading.Timer
急切计时器
/// <summary>
// EagerTimer is a simple wrapper around System.Timers.Timer that
// provides "set up and immediately execute" functionality by adding a
// new AutoStart property, and also provides the ability to manually
// raise the Elapsed event with RaiseElapsed.
/// </summary>
public class EagerTimer : Timer
{
public EagerTimer()
: base() { }
public EagerTimer(double interval)
: base(interval) { }
// Need to hide this so we can use Elapsed.Invoke below
// (otherwise the compiler complains)
private event ElapsedEventHandler _elapsedHandler;
public new event ElapsedEventHandler Elapsed
{
add { _elapsedHandler += value; base.Elapsed += value; }
remove { _elapsedHandler -= value; base.Elapsed -= value; }
}
public new void Start()
{
// If AutoStart is enabled, we need to invoke the timer event manually
if (AutoStart)
{
this._elapsedHandler.BeginInvoke(this, null, new AsyncCallback(AutoStartCallback), _elapsedHandler); // fire immediately
}
// Proceed as normal
base.Start();
}
private void AutoStartCallback(IAsyncResult result)
{
ElapsedEventHandler handler = result.AsyncState as ElapsedEventHandler;
if (handler != null) handler.EndInvoke(result);
}
// Summary:
// Gets or sets a value indicating whether the EagerTimer should raise
// the System.Timers.Timer.Elapsed event immediately when Start() is called,
// or only after the first time it elapses. If AutoStart is false, EagerTimer behaves
// identically to System.Timers.Timer.
//
// Returns:
// true if the EagerTimer should raise the System.Timers.Timer.Elapsed
// event immediately when Start() is called; false if it should raise the System.Timers.Timer.Elapsed
// event only after the first time the interval elapses. The default is true.
[Category("Behavior")]
[DefaultValue(true)]
[TimersDescription("TimerAutoStart")]
public bool AutoStart { get; set; }
/// <summary>
/// Manually raises the Elapsed event of the System.Timers.Timer.
/// </summary>
public void RaiseElapsed()
{
if (_elapsedHandler != null)
_elapsedHandler(this, null);
}
}
[TestClass]
public class Objects_EagerTimer_Tests
{
private const int TimerInterval = 10; // ms
private List<DateTime> _timerFires = new List<DateTime>();
private DateTime _testStart;
[TestInitialize]
public void TestSetup()
{
_timerFires.Clear();
_testStart = DateTime.Now;
}
[TestMethod]
public void Objects_EagerTimer_WithAutoStartDisabled()
{
// EagerTimer should behave as a normal System.Timers.Timer object
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.Elapsed += timerElapsed;
timer.Start();
// Wait (not enough time for first interval)
Thread.Sleep(5);
Assert.IsFalse(_timerFires.Any());
// Wait a little longer
Thread.Sleep(TimerInterval);
Assert.AreEqual(1, _timerFires.Count);
}
[TestMethod]
public void Objects_EagerTimer_WithAutoStartEnabled()
{
// EagerTimer should fire immediately on Start()
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.AutoStart = true;
timer.Elapsed += timerElapsed;
timer.Start();
// Wait (not enough time for first interval)
Thread.Sleep(5);
Assert.IsTrue(_timerFires.Any());
// Wait a little longer, now it will have fired twice
Thread.Sleep(TimerInterval);
Assert.AreEqual(2, _timerFires.Count);
}
[TestMethod]
public void Objects_EagerTimer_WhenRaisingManually()
{
// EagerTimer should fire immediately on Start()
var timer = new EagerTimer(TimerInterval);
timer.AutoReset = false;
timer.AutoStart = false;
timer.Elapsed += timerElapsed;
Assert.IsFalse(_timerFires.Any());
timer.RaiseElapsed();
Assert.IsTrue(_timerFires.Any());
}
private void timerElapsed(object sender, ElapsedEventArgs e) {
_timerFires.Add(DateTime.Now);
}
}