Интересно все разные ответы. Вот простой вариант DIY, который не зависит от каких-либо других библиотек и не требует ненужных ресурсов потоков.
Обычно для каждого действия в вашем списке создается функция onTick, которая выполняет это действие, а затем рекурсивно вызывает DoThings с оставшимися действиями и задержками.
Здесь ITimer
- это простая оболочка вокруг DispatcherTimer
(но она также будет работать с SWF-таймером или фиктивным таймером для модульного тестирования), а DelayedAction
- это просто кортеж с int Delay
и Action action
public static class TimerEx {
public static void DoThings(this ITimer timer, IEnumerable<DelayedAction> actions) {
timer.DoThings(actions.GetEnumerator());
}
static void DoThings(this ITimer timer, IEnumerator<DelayedAction> actions) {
if (!actions.MoveNext())
return;
var first = actions.Current;
Action onTick = null;
onTick = () => {
timer.IsEnabled = false;
first.Action();
// ReSharper disable AccessToModifiedClosure
timer.Tick -= onTick;
// ReSharper restore AccessToModifiedClosure
onTick = null;
timer.DoThings(actions);
};
timer.Tick += onTick;
timer.Interval = first.Delay;
timer.IsEnabled = true;
}
}
Если вы не хотите углубляться в F #, ссылаться на Rx или использовать .Net 4.5, это простое жизнеспособное решение.
Вот пример того, как это проверить:
[TestClass]
public sealed class TimerExTest {
[TestMethod]
public void Delayed_actions_should_be_scheduled_correctly() {
var timer = new MockTimer();
var i = 0;
var action = new DelayedAction(0, () => ++i);
timer.DoThings(new[] {action, action});
Assert.AreEqual(0, i);
timer.OnTick();
Assert.AreEqual(1, i);
timer.OnTick();
Assert.AreEqual(2, i);
timer.OnTick();
Assert.AreEqual(2, i);
}
}
А вот другие классы для компиляции:
public interface ITimer {
bool IsEnabled { set; }
double Interval { set; }
event Action Tick;
}
public sealed class Timer : ITimer {
readonly DispatcherTimer _timer;
public Timer() {
_timer = new DispatcherTimer();
_timer.Tick += (sender, e) => OnTick();
}
public double Interval {
set { _timer.Interval = TimeSpan.FromMilliseconds(value); }
}
public event Action Tick;
public bool IsEnabled {
set { _timer.IsEnabled = value; }
}
void OnTick() {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
public sealed class MockTimer : ITimer {
public event Action Tick;
public bool IsEnabled { private get; set; }
public double Interval { set { } }
public void OnTick() {
if (IsEnabled) {
var handler = Tick;
if (handler != null) {
handler();
}
}
}
}
public sealed class DelayedAction {
readonly Action _action;
readonly int _delay;
public DelayedAction(int delay, Action action) {
_delay = delay;
_action = action;
}
public Action Action {
get { return _action; }
}
public int Delay {
get { return _delay; }
}
}