2

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

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

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

4

5 回答 5

3

也许您正在尝试重新发明反应轮

确保您已安装并引用了响应式扩展,并且这些命名空间在范围内:

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 的相关部分

于 2012-05-11T02:18:15.623 回答
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;
    }
}
于 2012-05-11T00:28:22.127 回答
1

根据您的逻辑变得多么复杂,您可能需要考虑使用状态机实现。我自己的状态机实现了时间事件,因此您可以说,例如:

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

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

每秒对该方法的简单调用Tick()将导致状态机根据需要前进,或者更好的是,您可以休眠或设置一个计时器,以便下次它显示需要执行操作。

如果您需要对其他触发器进行编程以取消预定的事件、安排新的事件或只是像普通状态机一样运行,您也可以这样做。它还支持分层状态。

如果您需要能够保存状态和恢复并让所有计划的事件正常进行,那么它也是为这种情况而设计的(例如,需要在重新启动后幸存的 NT 服务)。

您可以从Nuget获取源代码。

于 2012-05-11T00:50:10.513 回答
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或其他一些类型并传入您需要的任何内容。

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

希望这可以帮助。

于 2012-05-11T00:55:52.427 回答
1

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

于 2012-05-10T23:52:22.963 回答