2

I have a function OnCreate() that runs once every second. OnCreate calls other functions based on the current time (current second). But instead of having to write each function individually inside of OnCreate, I would like to be able to group together functions i.e. in OnCreate at the 10 second mark I want to call a function CreateFiveX's which creates a new X starting now, and continues to do so at each new second for 5 seconds. Then at the 20 second mark I want OnCreate to call the same function again.

I would like to solve this problem using Events and Delegates. I'm thinking that at the start of OnCreate I want to fire an event that takes the current time and sends it to all subscribers and if one matches (i.e. the time they should fire matches the current time) they fire and are removed from the subscribing list.

The problem is that after too much time I still cannot figure out how to do this, and to make it even harder I would like to pass extra parameters to the functions that OnCreate calls. Any and all help would be nice, thanks.

Nasreddine
  • 36,610
  • 17
  • 75
  • 94
ryanb9
  • 211
  • 3
  • 4
  • 1
    What exactly do you want to do? Call `CreateFiveX()` every ten seconds? How is that “grouping functions together”? – svick May 10 '12 at 23:43
  • Inside OnCreate() I want to say if(timer == 10) CreateFiveX(); if(timer == 20) CreateFiveX(); so then an X is made at second 10,11,12,13,14,20,21,22,23, and 24. So basically CreateFiveX() would add 5 functions (all the same function, just 5 times) that need to be fired at an event i.e. timer == 21,22,23, and 24 respectively. – ryanb9 May 11 '12 at 00:00
  • Depends on your comfort level, but for this type of time-based composition, I would normally use the Reactive Extensions – James Manning May 11 '12 at 02:05

5 Answers5

3

Perhaps you are trying to reinvent the reactive wheel?

Make sure you have Reactive Extensions installed and referenced, and these namespaces in scope:

System.Reactive
System.Reactive.Linq

Then you can use (as an example) the following combinators joined together:

  • Observable.Interval(timespan) to create events every timespan
  • Observable.Take(number) to limit those (or other events) to a particular number
  • Observable.Timer() to create a single event at a particular time or time offset, from now or a future/past time
  • Observable.Do(action) and Observable.Subscribe(action) to perform side effects or other types of actions based on the streams
  • Observable.Repeat(number) to repeat one of the above a number of times
  • Observable.Generate() to generate observable sequences from state

And then many others, including those to create observables from enumerables or from things like event patterns, and many many more.

If you have a concrete example, we can show you how to get it done using Rx. You can also browse the relevant section of StackOverflow.

Community
  • 1
  • 1
yamen
  • 15,390
  • 3
  • 42
  • 52
  • Yes that is what I am trying to reinvent or at least learn how to reinvent. Thanks, and thanks to all the other posters I am soaking in everyone's suggestions. Edit: I am not so much trying to reinvent as much as learn how things are done xD The easy way is to just make a thread and have it wait 1 second while it loops 5 times but I am interested in different ways to solve the same problem. – ryanb9 May 11 '12 at 04:36
  • This is a complex area. Learning Rx is challenging and rewarding enough, more so than trying to do it from scratch. This way you learn and have a very handy tool also. – yamen May 11 '12 at 06:30
1

You could create several timers for each interval you want. Or OnCreate() could check the system time and perform a Math.Mod() and put the output into a switch statement that calls the correct functions at certain times. If you want some sample code, I can write some up for you.

MrWuf
  • 1,478
  • 1
  • 14
  • 30
  • Thanks but I would like to solve the problem using Events and Delegates because I think the problem is solvable by using them but I cant quite figure out how. – ryanb9 May 10 '12 at 23:55
1

Sample of code using events, and parameterized handlers:

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;
    }
}
JotaBe
  • 38,030
  • 8
  • 98
  • 117
1

Depending on how complex your logic becomes you might want to consider a state machine implementation instead. My own state machine implements temporal events so you can say, for example:

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

Or you could schedule 5 events in a row using the .At method.

A simple call to the Tick() method every second will cause the state machine to advance as necessary, or better yet, you can sleep or set a timer for the next time it shows that an action is required.

If you need to program other triggers to cancel scheduled events, schedule new events or simply behave like a normal state machine you can do that too. It also supports hierarchical states.

If you need to be able to save state and resume and have all of your scheduled events carry on as normal it's also designed for that scenario (e.g. an NT service that needs to survive restarts).

You can get the source code from Nuget.

Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
1

I thought this was an interesting question, and although I don't quite understand your explanation about the function that executes every second but calls another function that runs for five seconds, it seems to me that the piece you're missing is how to encapsulate a method call. So I whipped up this sample that uses a timer and Action delegates that are called at predetermined timer ticks. You should be able to extrapolate this into your design if it's what you're looking for.

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

This is the output:

----------------------
Hit any key to exit
5: 634722692898378113
8: 634722692928801155
10: 634722692949083183
13: 634722692979496224
Finished at 13 seconds

(this works because the timer is still running while the console is waiting for a keypress)

Note that while I used the same Action delegate instance for all the TimedFunction objects, nothing prevents you from using different ones. While you do need to define the types of arguments the delegates will take, you can always just use object or some other type and pass in whatever you need.

Also, there's no error checking and you didn't mention what type of application you're doing this in; you'll probably want to use a separate thread, for example. Oh, and timer resolution isn't really that good so be careful.

Hope this helps.

kprobst
  • 16,165
  • 5
  • 32
  • 53