简体   繁体   中英

Timer in Windows 8.1 - how to mimic Timer(TimerCallback) constructor?

I am porting an existing .NET class library that uses System.Threading.Timer to a Windows Store app that targets Windows 8.1. The Timer class is available, but a few options seem to be missing in relation to the corresponding .NET Framework Timer .

In particular, there are only two constructors available in the Windows Store version:

public Timer(TimerCallback callback, Object state, int dueTime, int period);
public Timer(TimerCallback callback, Object state, TimeSpan dueTime, TimeSpan period);

.NET Framework contains this additional constructor:

public Timer(TimerCallback callback);

which according to the MSDN documentation sets dueTime and period to Timeout.Infinite and state to the Timer object itself.

Trying to replace the single argument constructor, I have "naively" tried to pass the Timer object into one of the Windows 8.1 constructors, like this:

Timer t;
t = new Timer(MyCallback, t, Timeout.Infinite, Timeout.Infinite);  // WILL NOT WORK!!!

but of course this only yields the compilation error

Use of unassigned local variable 't'

There is also no State setter or SetState method in the Timer class, so the state cannot be set after construction.

What can I do to fully mimic the full framework's Timer(TimerCallback) constructor?

Note that these options are acceptable as long as you start the timer manually after the field/property is set, which means using Timeout.Infinite for due time which you are.

State Object

Add a property to a state object:

public class MyState
{
   public Timer { get; set; }
}

//create empty state
MyState s = new MyState();
//create timer paused
Timer t = new Timer(MyCallback, s, Timeout.Infinite, Timeout.Infinite);
//update state
s.Timer = t;
//now safe to start timer
t.Change(..)

Private Field

_t = new Timer(MyCallback, null, Timeout.Infinite, Timeout.Infinite);

MyCallback(object state)
{
  // now have access to the timer _t
  _t.
}

Private Field of an Inner Class

And if one private field is not enough because you want to launch and track multiple, make a new class that wraps a timer. This could be an inner class:

public class ExistingClass
{
    public void Launch()
    {
        new TimerWrapper(this);
    }

    private sealed class TimerWrapper
    {
        private readonly ExistingClass _outer;
        private readonly Timer _t;

        public TimerWrapper(ExistingClass outer)
        {
            _outer = outer;
            //start timer
            _t = new Timer(state=>_outer.MyCallBack(this),
                           null, Timeout.Infinite, Timeout.Infinite);
        }

        public Timer Timer
        {
            get { return _t; }
        }
    }

    private void MyCallBack(TimerWrapper wrapper)
    {
        wrapper.Timer.
    }
}

You can use a closure. For example:

Timer t = null;

t = new Timer(
    _ => 
    {
        if (t != null)
        {
            // simply use t here, for example
            var temp = t;
        }
    },
    null,
    Timeout.Infinite, 
    Timeout.Infinite);

Notice how I test that t != null , just in case the timer already calls the callback before it has been assigned to the variable t , which might happen if you were to use 0 as a value for dueTime. With a value of Timeout.Infinite, that can't really happen, but I like being defensive in multithreaded scenarios.

In addition to t , you can use any other variable that is in scope when the timer is created, as they will all be lifted into the closure (when used in the callback).

If you only want a method to replace the missing constructor, easing your porting effort, here it is:

public static Timer Create(TimerCallback callback)
{
    Timer t = null;

    t = new Timer(_ => callback(t), null, Timeout.Infinite, Timeout.Infinite);

    return t;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM