【问题标题】:Problems with System.Threading.Timer in PCL Profile 78 - WarningsPCL Profile 78 中 System.Threading.Timer 的问题 - 警告
【发布时间】:2014-05-02 10:17:57
【问题描述】:

我很惊讶 Profile 78 库中不存在方便的 System.Threading.Timer 类。为了使用这个类,我创建了另一个针对 4.0 框架的 PCL,并编写了一个简单的包装器(正如一篇博文中所建议的那样):

public class PCLTimer
{
    private Timer timer;
    private Action<object> action;

    public PCLTimer (Action<object> action, object state, int dueTimeMilliseconds, int periodMilliseconds)
    {
        this.action = action;
        timer = new Timer (PCLTimerCallback, state, dueTimeMilliseconds, periodMilliseconds);
    }

    private void PCLTimerCallback (object state)
    {
        action.Invoke (state);
    }

    public bool Change (int dueTimeMilliseconds, int periodMilliseconds)
    {
        return timer.Change (dueTimeMilliseconds, periodMilliseconds);
    }
}

现在我可以引用这个 4.0 库并在主 PCL 库中使用 PCLTimer。但是当我尝试构建我的主要 Android 项目时,我收到以下警告:

Warning CS1684: Reference to type 'System.Threading.Timer' claims it is defined in 'c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile78\mscorlib.dll', but it could not be found (CS1684) (Prototype.Core)

Warning MSB3247: Found conflicts between different versions of the same dependent assembly. (MSB3247) (Prototype.Droid)

如何正确消除这些警告?

【问题讨论】:

  • 请参阅stackoverflow.com/questions/12555049/timer-in-portable-library - 包括来自 PCL 团队在 ms 中的更新
  • 是的,我看到了这个问题,并从接受的答案中实施了类似于第 3 个解决方案的方法。现在我在问这些警告。 4.5.1 PCL 库的修复怎么样 - Xamarin Studio 中还没有它还是什么?我的意思是我不能引用 Timer,它在我的 PCL 的 System.Threading 命名空间中不存在。
  • 我建议不要使用仅插入 .NET 4.0 Timer 的答案,而是使用 Task.Delay 构建您自己的计时器类,例如 stackoverflow.com/a/21095323/957673。这避免了完全引用 System.Threading.Timer 的问题。
  • 我喜欢 Timer 类,我在很多项目中都使用过它,它测试了无数次。我不想为这么简单的事情实现我自己的类。但如果什么都没有了,也许我不得不)

标签: c# android xamarin mvvmcross portable-class-library


【解决方案1】:

这是对在 Profile 78 中暂时消失的 Timer 类的完整重新实现,使用异步任务:

using System;
using System.Threading;
using System.Threading.Tasks;

namespace Quantum
{
    public delegate void TimerCallback(object state);

    public sealed class Timer : IDisposable
    {
        private static Task CompletedTask = Task.FromResult(false);

        private TimerCallback Callback;
        private Task Delay;
        private bool Disposed;
        private int Period;
        private object State;
        private CancellationTokenSource TokenSource;

        public Timer(TimerCallback callback, object state, int dueTime, int period)
        {
            Callback = callback;
            State = state;
            Period = period;
            Reset(dueTime);
        }

        public Timer(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
            : this(callback, state, (int)dueTime.TotalMilliseconds, (int)period.TotalMilliseconds)
        {
        }

        ~Timer()
        {
            Dispose(false);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool cleanUpManagedObjects)
        {
            if (cleanUpManagedObjects)
                Cancel();
            Disposed = true;
        }

        public void Change(int dueTime, int period)
        {
            Period = period;
            Reset(dueTime);
        }

        public void Change(TimeSpan dueTime, TimeSpan period)
        {
            Change((int)dueTime.TotalMilliseconds, (int)period.TotalMilliseconds);
        }

        private void Reset(int due)
        {
            Cancel();
            if (due >= 0)
            {
                TokenSource = new CancellationTokenSource();
                Action tick = null;
                tick = () =>
                {
                    Task.Run(() => Callback(State));
                    if (!Disposed && Period >= 0)
                    {
                        if (Period > 0)
                            Delay = Task.Delay(Period, TokenSource.Token);
                        else
                            Delay = CompletedTask;
                        Delay.ContinueWith(t => tick(), TokenSource.Token);
                    }
                };
                if (due > 0)
                    Delay = Task.Delay(due, TokenSource.Token);
                else
                    Delay = CompletedTask;
                Delay.ContinueWith(t => tick(), TokenSource.Token);
            }
        }

        private void Cancel()
        {
            if (TokenSource != null)
            {
                TokenSource.Cancel();
                TokenSource.Dispose();
                TokenSource = null;
            }
        }
    }
}

【讨论】:

    【解决方案2】:

    是否需要为 app.config 添加绑定?当我添加一个 WP8 项目时,我不得不为 HttpClient 做类似的事情。

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <dependentAssembly>
            <assemblyIdentity name="System.Net.Http" 
                              publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
            <bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="2.0.5.0" />
          </dependentAssembly>
        </assemblyBinding>
      </runtime>
    </configuration>
    

    【讨论】:

    • 我试图做类似的事情,但没有帮助。显然我对 mscorlib.dll 有一些问题。其中有两个 - 一个由 4.5 PCL 引用,一个来自 4.0 PCL。我需要弄清楚如何消除歧义。
    【解决方案3】:

    我没有将 Timer 包装器实现放入单独的 .net 4.0 项目中,而是以不同的方式解决了这个问题:

    我在核心项目中创建了一个 ITimerWrapper 接口,并在 Droid 和 WinPhone 项目中放置了单独的实现。然后我使用 IoC 在应用启动时设置所需的实现。

    使用此方法的Timer的不同PCL版本之间没有冲突。

    另一个优点是我现在可以在 WinPhone 项目中使用 DispatcherTimers - 我只能在 Android 和 WinPhone 之间共享代码时使用 System.Threading.Timers。

    【讨论】:

    • 我喜欢这个解决方案 :)
    猜你喜欢
    • 2013-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-21
    • 2011-08-07
    • 1970-01-01
    相关资源
    最近更新 更多