Таймер в портативной библиотеке
Я не могу найти таймер в портативной библиотеке /Windows Store. (Targeting.net 4.5 и Windows Store aka Metro)
Есть ли идеи о том, как создать какое-то событие синхронизации?
Мне нужно какое-то секундомер, поэтому он должен обновляться раз в секунду или около того
Ответы
Ответ 1
Обновление: Мы исправили это в Visual Studio 2013. Портативные библиотеки, предназначенные для проектов Store (Windows 8.1) и .NET Framework 4.5.1, теперь могут ссылаться на Timer.
Это неудачный случай, когда наши детали реализации протекают пользователю. Когда вы нацеливаетесь только на приложения .NET 4.5 и Windows Store, мы на самом деле вынуждаем вас создавать что-то другое, когда вы нацеливаете платформу нижнего уровня (.NET 4, SL 4/5, Phone 7.x). Мы стараемся рассматривать эти два как одно и то же, но ограниченные изменения под началом утечки (такие как Timer и Reflection). Мы рассмотрим некоторые из них здесь: http://channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries.
Мы рассмотрим исправление этого в будущей версии. До тех пор у вас есть несколько обходных решений:
1) Внесите свою собственную версию таймера, используя Task.Delay, здесь мы быстро используем копию:
internal delegate void TimerCallback(object state);
internal sealed class Timer : CancellationTokenSource, IDisposable
{
internal Timer(TimerCallback callback, object state, int dueTime, int period)
{
Contract.Assert(period == -1, "This stub implementation only supports dueTime.");
Task.Delay(dueTime, Token).ContinueWith((t, s) =>
{
var tuple = (Tuple<TimerCallback, object>)s;
tuple.Item1(tuple.Item2);
}, Tuple.Create(callback, state), CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
public new void Dispose() { base.Cancel(); }
}
2) Снизьте свой проект до приложений .NET 4.0 и Windows Store, что даст вам доступ к таймеру.
3) Создайте новый проект, ориентированный на приложения .NET 4.0 и Windows Store, и поместите в него код, требующий таймера. Затем обратитесь к проекту приложений .NET 4.5 и Windows Store.
В качестве дополнительной заметки я написал для себя элемент работы для себя на сайте PclContrib, чтобы добавить поддержку таймера: http://pclcontrib.codeplex.com/workitem/12513.
Ответ 2
Следующее предложение № 3 от Дэвида Кина, здесь мой взломанный адаптер таймера - поместите это в библиотеку PCL, которая нацелена на .net 4.0, и ссылается на нее из 4.5:
public class PCLTimer
{
private Timer _timer;
private Action _action;
public PCLTimer(Action action, TimeSpan dueTime, TimeSpan period)
{
_action = action;
_timer = new Timer(PCLTimerCallback, null, dueTime, period);
}
private void PCLTimerCallback(object state)
{
_action.Invoke();
}
public bool Change(TimeSpan dueTime, TimeSpan period)
{
return _timer.Change(dueTime, period);
}
}
И затем, чтобы использовать его, вы можете сделать это из своей библиотеки 4.5 PCL:
private void TimeEvent()
{
//place your timer callback code here
}
public void SetupTimer()
{
//set up timer to run every second
PCLTimer _pageTimer = new PCLTimer(new Action(TimeEvent), TimeSpan.FromMilliseconds(-1), TimeSpan.FromSeconds(1));
//timer starts one second from now
_pageTimer.Change(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1));
}
Ответ 3
Реализация предложения №1 от Дэвида Кина с периодом:
public delegate void TimerCallback(object state);
public sealed class Timer : CancellationTokenSource, IDisposable
{
public Timer(TimerCallback callback, object state, int dueTime, int period)
{
Task.Delay(dueTime, Token).ContinueWith(async (t, s) =>
{
var tuple = (Tuple<TimerCallback, object>) s;
while (true)
{
if (IsCancellationRequested)
break;
Task.Run(() => tuple.Item1(tuple.Item2));
await Task.Delay(period);
}
}, Tuple.Create(callback, state), CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.Default);
}
public new void Dispose() { base.Cancel(); }
}
Ответ 4
Я улучшил ответ Иван Леоненко, включив новый параметр, который в очереди вызывает обратный вызов, если период меньше времени обратного вызова. И заменил устаревший TimerCallback действием. И, наконец, используйте наш маркер отмены за последнюю задержку и используйте ConfigureAwait для увеличения concurrency, так как обратный вызов может быть выполнен в любом потоке.
internal sealed class Timer : CancellationTokenSource
{
internal Timer(Action<object> callback, object state, int millisecondsDueTime, int millisecondsPeriod, bool waitForCallbackBeforeNextPeriod = false)
{
//Contract.Assert(period == -1, "This stub implementation only supports dueTime.");
Task.Delay(millisecondsDueTime, Token).ContinueWith(async (t, s) =>
{
var tuple = (Tuple<Action<object>, object>) s;
while (!IsCancellationRequested)
{
if (waitForCallbackBeforeNextPeriod)
tuple.Item1(tuple.Item2);
else
Task.Run(() => tuple.Item1(tuple.Item2));
await Task.Delay(millisecondsPeriod, Token).ConfigureAwait(false);
}
}, Tuple.Create(callback, state), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default);
}
protected override void Dispose(bool disposing)
{
if(disposing)
Cancel();
base.Dispose(disposing);
}
}
Ответ 5
Вы можете создать интерфейс таймера с помощью библиотеки PCL, а затем создать реализацию этого интерфейса во второй библиотеке W8S с использованием таймера W8S.
Затем вы можете использовать инъекцию зависимостей, чтобы вставить библиотеку W8S в класс PCL.
Ответ 6
Я закончил с Observable.Timer
из Reactive Extensions (Rx). Rx уже включен в проект, поэтому дополнительная ссылка не была проблемой.
Вот таймер, который запускается каждую секунду:
IDisposable timer = Observable.Timer(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(1))
.Subscribe(_ => /* your useful code here */);
// unsubscribe/stop when timer is no longer needed
timer.Dispose();
System.Reactive.Linq.Observable
класс находится в формате PCL Rx-Linq
Пакет NuGet.