Подтвердить что ты не робот

Таймер в портативной библиотеке

Я не могу найти таймер в портативной библиотеке /Windows Store. (Targeting.net 4.5 и Windows Store aka Metro)

Есть ли идеи о том, как создать какое-то событие синхронизации?

Мне нужно какое-то секундомер, поэтому он должен обновляться раз в секунду или около того

4b9b3361

Ответ 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.