C# 便携式图书馆中的计时器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12555049/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:40:13  来源:igfitidea点击:

Timer in Portable Library

c#timermicrosoft-metroportable-class-library

提问by Boas Enkler

I can't find a timer in portable library / Windows Store. (Targeting .net 4.5 and Windows Store aka Metro)

我在便携式库/Windows 应用商店中找不到计时器。(针对 .net 4.5 和 Windows Store aka Metro)

Does any have an idea on how to created some kind of timing event?

有没有人知道如何创建某种计时事件?

I need somekind of a stopwatch, so this should refreshn once a second or so

我需要一个秒表,所以它应该每秒刷新一次

采纳答案by David Kean

Update:We have fixed this in Visual Studio 2013. Portable libraries targeting Store (Windows 8.1) and .NET Framework 4.5.1 projects can now reference Timer.

更新:我们在 Visual Studio 2013 中修复了这个问题。面向 Store (Windows 8.1) 和 .NET Framework 4.5.1 项目的便携式库现在可以引用 Timer。

This is unfortunate case of where our implementation details are leaking to the user. When you target just .NET 4.5 and Windows Store apps, we actually cause you to build against something different then when you target a down-level platform (.NET 4, SL 4/5, Phone 7.x). We try treat these two as the same, but limited changes underneath start to leak (such as Timer, and Reflection). We cover some of this here: http://channel9.msdn.com/Shows/Going+Deep/NET-45-David-Kean-and-Marcea-Trofin-Portable-Libraries.

这是我们的实现细节泄露给用户的不幸案例。当您只针对 .NET 4.5 和 Windows 应用商店应用程序时,我们实际上会导致您针对与下层平台(.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

We'll look at fixing this in a future version. Until then, you have a couple of workarounds:

我们将在未来的版本中考虑修复这个问题。在此之前,您有几个解决方法:

1) Implement your own version of Timer using Task.Delay, here's a quick copy that we're using internally:

1) 使用 Task.Delay 实现您自己的 Timer 版本,这是我们内部使用的快速副本:

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) Downgrade your project to .NET 4.0 and Windows Store apps, which will give you access to Timer.

2) 将您的项目降级到 .NET 4.0 和 Windows Store 应用程序,这样您就可以访问 Timer。

3) Create a new project targeting .NET 4.0 and Windows Store apps, and put the code that requires timer in that. Then reference that from the .NET 4.5 and Windows Store apps project.

3) 创建一个针对.NET 4.0 和Windows Store 应用程序的新项目,并将需要计时器的代码放入其中。然后从 .NET 4.5 和 Windows Store 应用程序项目中引用它。

As a side note, I've filed a work item for myself over on PclContrib site to add Timer support: http://pclcontrib.codeplex.com/workitem/12513.

作为旁注,我已经在 PclContrib 站点上为自己提交了一个工作项目以添加计时器支持:http://pclcontrib.codeplex.com/workitem/12513 。

回答by Henry C

Following suggestion #3 from David Kean, here's my hacky Timer adapter - put this in a PCL library that targets .net 4.0, and reference it from 4.5:

遵循 David Kean 的建议 #3,这是我的 hacky Timer 适配器 - 将其放入针对 .net 4.0 的 PCL 库中,并从 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);
        }
    }

And then to use it, you can do this from your 4.5 PCL library:

然后要使用它,您可以从 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));

    }

回答by Paul

You could create a timer interface using a PCL library and then create an implementation of that interface in a second W8S library using a W8S timer.

您可以使用 PCL 库创建计时器接口,然后使用 W8S 计时器在第二个 W8S 库中创建该接口的实现。

Then you could use dependency injection to inject the W8S library into the PCL class.

然后你可以使用依赖注入将 W8S 库注入到 PCL 类中。

回答by Ivan Leonenko

Implementation of suggestion #1 from David Kean with period:

执行 David Kean 的建议 #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(); }
}

回答by Softlion

I improved Ivan Leonenko answer by including a new parameter, which queue calls to you callback if the period is less than the callback run time. And replaced the legacy TimerCallback with an action. And finally, use our cancel token in the last delay, and used ConfigureAwait to increase concurrency, as the callback can be executed on any thread.

我通过包含一个新参数改进了 Ivan Leonenko 的答案,如果周期小于回调运行时间,该参数将调用您的回调。并用一个动作替换了旧的 TimerCallback。最后,在最后一个延迟中使用我们的取消令牌,并使用 ConfigureAwait 来增加并发性,因为回调可以在任何线程上执行。

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);
    }
}

回答by altso

I ended up with Observable.Timerfrom Reactive Extensions (Rx). Rx was already included in the project, so additional reference was not an issue.

我最终使用Observable.Timer了 Reactive Extensions (Rx)。Rx 已经包含在项目中,所以额外的参考不是问题。

Here is a timer that triggers every second:

这是一个每秒触发的计时器:

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.Observableclass is in PCL friendly Rx-LinqNuGet package.

System.Reactive.Linq.Observable类在 PCL 友好的Rx-LinqNuGet 包中。