基于时间的 C# 自定义事件
本文关键字:自定义 事件 时间 于时间 | 更新日期: 2023-09-27 18:33:45
我有一个每秒运行一次的函数OnCreate()
。 OnCreate
根据当前时间(当前秒(调用其他函数。但是,我希望能够将函数组合在一起,而不是在OnCreate
中单独编写每个函数,即在 10 秒标记处OnCreate
我想调用一个函数CreateFiveX
函数,它从现在开始创建一个新的 X,并在每一秒继续这样做 5 秒。然后在 20 秒标记处OnCreate
我希望再次调用相同的函数。
我想使用事件和委托来解决这个问题。我想在OnCreate
开始时,我想触发一个占用当前时间并将其发送给所有订阅者的事件,如果一个匹配(即他们应该触发的时间与当前时间匹配(,它们会触发并从订阅列表中删除。
问题是,经过太多时间,我仍然无法弄清楚如何做到这一点,并且为了使它更加困难,我想将额外的参数传递给OnCreate
调用的函数。任何和所有的帮助都会很好,谢谢。
也许您正在尝试重新发明反应轮?
确保已安装并引用了反应式扩展,并且范围内的这些命名空间:
System.Reactive
System.Reactive.Linq
然后,您可以使用(作为示例(以下组合器连接在一起:
-
Observable.Interval(timespan)
每timespan
创建事件 -
Observable.Take(number)
将这些(或其他事件(限制为特定number
-
Observable.Timer()
在特定时间或时间偏移量(从现在或将来/过去的时间(创建单个事件 -
Observable.Do(action)
和Observable.Subscribe(action)
,以根据流执行副作用或其他类型的操作 -
Observable.Repeat(number)
多次重复上述其中一项 -
Observable.Generate()
从状态生成可观察序列
然后是许多其他的,包括那些从可枚举量或事件模式等内容创建可观察量的人,等等。
如果你有一个具体的例子,我们可以告诉你如何使用Rx完成它。您还可以浏览StackOverflow的相关部分。
您可以为所需的每个间隔创建多个计时器。 或者OnCreate()
可以检查系统时间并执行Math.Mod()
,并将输出放入在特定时间调用正确函数的 switch
语句中。 如果你想要一些示例代码,我可以为你写一些。
使用事件和参数化处理程序的代码示例:
public class TheTimer
{
// define delegate with desired parameters
public delegate void TimerDelegate(int param1, string param2);
// expose the event
public event TimerDelegate OnTimer;
// This is the handler of a regular Timer tick:or your OnCreate() function
Timer1_Tick()
{
if (OnTimer != null) // if susbcribed handlers
{
OnTimer(1, "value"); // raise your cutom event, passing the params
}
}
}
// Implement classes with functions that can handle the event
public class HandlerFunctions
{
public void HandleTimer(int p, string p2)
{
// do whatever
}
}
// Use your classes
public class TimerUserClass
{
public static void UseTimers()
{
// Create objects
TheTimer theTimer = new TheTimer();
HandlerFunctions handler1 = new HandlerFunctions();
HandlerFunctions handler2 = new HandlerFunctions();
// SUbscribe events
theTimer.OnTimer += handler1.HandleTimer;
theTimer.OnTimer += handler2.HandleTimer;
// un-susbcribe
theTimer.OnTimer -= handler1.HandleTimer;
}
}
根据逻辑的复杂程度,您可能需要改为考虑状态机实现。 我自己的状态机实现了时态事件,因此您可以说,例如:
machine.Every(new TimeSpan(hours: 0, minutes:0, seconds:5), eScheduledCheck);
或者,您可以使用 .At
方法连续安排 5 个事件。
每秒对 Tick()
方法进行一次简单的调用将导致状态机根据需要前进,或者更好的是,您可以休眠或设置计时器,以便下次显示需要操作。
如果您需要对其他触发器进行编程以取消计划事件、计划新事件或只是像普通状态机一样运行,您也可以这样做。 它还支持分层状态。
如果您需要能够保存状态和恢复,并让所有计划事件正常进行,则它也是为这种情况而设计的(例如,需要在重新启动后幸存下来的 NT 服务(。
可以从 Nuget 获取源代码。
这是一个有趣的问题,虽然我不太明白你对每秒执行一次但调用另一个运行五秒的函数的解释,但在我看来,你缺少的部分是如何封装方法调用。因此,我制作了这个示例,该示例使用计时器和Action
在预定计时器时钟周期调用的委托。如果这是您正在寻找的,您应该能够将其推断到您的设计中。
class TimedFunction
{
public Action<TimedFunction, object> Method;
public int Seconds = 0;
public TimedFunction() {
}
}
class Program
{
static int _secondsElapsed = 0;
static List<TimedFunction> _funcs = new List<TimedFunction>();
static int _highestElapsed = 0;
static Timer _timer;
static void Main(string[] args) {
var method = new Action<TimedFunction, object>((tf, arg) => Console.WriteLine("{0}: {1}", tf.Seconds, arg));
_funcs.Add(new TimedFunction() { Seconds = 5, Method = method });
_funcs.Add(new TimedFunction() { Seconds = 8, Method = method });
_funcs.Add(new TimedFunction() { Seconds = 13, Method = method });
_funcs.Add(new TimedFunction() { Seconds = 10, Method = method });
_highestElapsed = _funcs.Max(tf => tf.Seconds);
_timer = new Timer(1000);
_timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
_timer.Start();
Console.WriteLine();
Console.WriteLine("----------------------");
Console.WriteLine("Hit any key to exit");
Console.ReadKey(true);
}
static void t_Elapsed(object sender, ElapsedEventArgs e) {
_secondsElapsed++;
foreach (TimedFunction tf in _funcs) {
if (tf.Seconds == _secondsElapsed) {
tf.Method(tf, DateTime.Now.Ticks);
}
}
if (_secondsElapsed > _highestElapsed) {
Console.WriteLine("Finished at {0} seconds", _secondsElapsed - 1);
_timer.Stop();
}
}
}
这是输出:
----------------------
Hit any key to exit
5: 634722692898378113
8: 634722692928801155
10: 634722692949083183
13: 634722692979496224
Finished at 13 seconds
(这是有效的,因为计时器仍在运行,而控制台正在等待按键(
请注意,虽然我对所有TimedFunction
对象使用了相同的 Action
委托实例,但没有什么可以阻止您使用不同的对象。虽然您确实需要定义委托将采用的参数类型,但您始终可以使用object
或其他类型并传入所需的任何内容。
错误检查,您没有提到您正在执行此操作的应用程序类型;例如,您可能希望使用单独的线程。哦,计时器分辨率并不是那么好,所以要小心。
希望这有帮助。