Home > OS >  C# System.Timers.Timer array countdown
C# System.Timers.Timer array countdown

Time:01-25

static void Main()
{
    int timersLength = 4;
    int interval = 1000;
    int[] timerNum = new int[timersLength];
    Timer[] timers = new Timer[timersLength];

    for (int i = 0; i < timersLength; i  )
    {
        timerNum[i] = i;
        timers[i] = new Timer(interval);
        //Console.WriteLine($"Timer {timerNum[i]} is running");
        timers[i].Elapsed  = (o, e) =>
        {
            Console.WriteLine($"Timer {timerNum[i]} is running");
        };
    }

    foreach (Timer timer in timers)
        timer.Start();

    Console.ReadKey();
}

whenever I'm trying to make a countdown to the Timer array it gives an

"System.IndexOutOfRangeException: 'Index was outside the bounds of the array."

on the Elapsed line:

timers[i].Elapsed  = (o, e) =>
{
    Console.WriteLine($"Timer {timerNum[i]} is running");
};

The code seems fine but for some reason it gives this error.

CodePudding user response:

The reason is that you use the loop variable i in the timer callback, which is always 4 after the loop, so causing that exception. You should use the Timer instance passed:

timers[i].Elapsed  = (o, e) =>
{
    Console.WriteLine($"Timer {o} is running");
};

However, that doesn't make much sense since Timer.ToString will just output it's type.

If you want a meaningful output you could make your own NamedTimer class:

public class NamedTimer : System.Timers.Timer
{
    public string Name { get; }

    public NamedTimer(double interval, string name): base(interval)
    {
        this.Name = name;
    }

    public override string ToString() => Name;
}

You use it in this way:

NamedTimer[] timers = new NamedTimer[timersLength];

for (int i = 0; i < timersLength; i  )
{
    timers[i] = new NamedTimer(interval, (i 1).ToString());
    timers[i].Elapsed  = (o, e) =>
    {
        Console.WriteLine($"Timer {o} is running");
    };
}

CodePudding user response:

If you don't want to create a class to store the name of the Timer next to it then you can simply create a Dictionary<Timer, string> collection for mapping.

Dictionary<Timer, string> timers = new Dictionary<Timer, string>(timersLength);

for (int i = 0; i < timersLength; i  )
{
    var timer = new Timer(interval);
    timers[timer] = $"Timer {i}";
    timer.Elapsed  = (sender, _) => Console.WriteLine($"{timers[(Timer)sender]} is running");
}

foreach (KeyValuePair<Timer, string> mapping in timers)
    mapping.Key.Start();
  • The Elapsed event handler will be called by passing the sender object
  • The sender's type is object so you need to cast it to Timer
  • That object can be used to do the look up in the timers collection
  • Since yo don't use the EventArgs of the Elapsed I suggest to use the discard operator there to express your intent more clearly
  •  Tags:  
  • Related