C#基于时间的自定义事件

时间:2012-05-10 23:33:38

标签: c# events delegates timer eventargs

我有一个每秒运行一次的函数OnCreate()OnCreate根据当前时间(当前秒)调用其他函数。但是我不想在OnCreate内单独编写每个函数,而是希望能够将函数组合在一起,即在OnCreate中在10秒标记处我想调用函数CreateFiveX现在开始创建一个新的X,并在每个新的第二个继续这样做5秒。然后在20秒标记处,我希望OnCreate再次调用相同的函数。

我想使用Events和Delegates解决这个问题。我想在OnCreate开始时我想发一个占用当前时间并将其发送给所有订阅者的事件,如果一个匹配(即它们应该触发的时间与当前时间相匹配),它们会触发从订阅列表中删除。

问题是,经过太多时间后我仍然无法弄清楚如何做到这一点,并且为了让它变得更加困难,我想将额外的参数传递给OnCreate调用的函数。任何和所有帮助都会很好,谢谢。

5 个答案:

答案 0 :(得分:3)

也许您正在尝试reinvent the reactive wheel

确保安装并引用了Reactive Extensions,以及范围内的这些命名空间:

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() 从州生成可观察的序列

然后是其他许多人,包括create observables from enumerablesevent patternsmany many more等内容。

如果你有一个具体的例子,我们可以告诉你如何使用Rx完成它。您还可以浏览the relevant section of StackOverflow

答案 1 :(得分:1)

您可以为所需的每个间隔创建多个计时器。或者OnCreate()可以检查系统时间并执行Math.Mod()并将输出放入switch语句中,该语句在特定时间调用正确的函数。如果你想要一些示例代码,我可以为你写一些代码。

答案 2 :(得分:1)

使用事件和参数化处理程序的代码示例:

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

答案 3 :(得分:1)

根据逻辑的复杂程度,您可能需要考虑状态机实现。我的own state machine实现了时间事件,因此您可以说,例如:

 machine.Every(new TimeSpan(hours: 0, minutes:0, seconds:5), eScheduledCheck);

或者您可以使用.At方法连续安排5个事件。

每秒对Tick()方法的简单调用将导致状态机在必要时前进,或者更好的是,您可以在下次显示需要操作时睡眠或设置计时器。 / p>

如果您需要编写其他触发器以取消预定事件,安排新事件或只是像普通状态机那样操作,您也可以这样做。它还支持分层状态。

如果您需要能够保存状态和恢复,并且所有预定事件都正常进行,那么它也是为该场景设计的(例如需要在重新启动后继续运行的NT服务)。

您可以从Nuget获取源代码。

答案 4 :(得分:1)

我认为这是一个有趣的问题,虽然我不太明白你对每秒执行的函数的解释但是调用另一个运行五秒钟的函数,但在我看来,你丢失的那个是如何封装方法调用。所以我掀起了这个使用计时器和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

(这是因为在控制台等待按键时计时器仍在运行)

请注意,虽然我为所有Action对象使用了相同的TimedFunction委托实例,但没有什么可以阻止您使用不同的object对象。虽然您确实需要定义委托将采用的参数类型,但您始终可以使用{{1}}或其他类型并传入您需要的任何类型。

此外,没有错误检查,你没有提到你正在做什么类型的应用程序;例如,你可能想要使用一个单独的线程。哦,定时器分辨率并不是那么好,所以要小心。

希望这有帮助。