问题
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?
回答1:
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.
}
}
回答2:
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;
}
来源:https://stackoverflow.com/questions/19703993/timer-in-windows-8-1-how-to-mimic-timertimercallback-constructor