Проблемы с System.Threading.Timer в профиле PCL 78 - Предупреждения

Меня удивило, что удобного класса System.Threading.Timer нет в библиотеках Profile 78. Чтобы использовать этот класс, я создал еще один PCL, нацеленный на платформу 4.0, и написал простую оболочку (как было предложено в одном сообщении в блоге):

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 и использовать PCLTimer в основной библиотеке PCL. Но когда я пытаюсь собрать свой основной 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)

Как правильно избавиться от этих предупреждений?


person Aleksei Petrenko    schedule 24.03.2014    source источник
comment
См. stackoverflow.com/questions/12555049/timer-in-portable- библиотека - включая обновление от команды PCL в мс   -  person Stuart    schedule 25.03.2014
comment
Да, я видел этот вопрос и реализовал что-то вроде третьего решения из принятого ответа. Теперь я спрашиваю об этих предупреждениях. А как насчет исправления для библиотек PCL 4.5.1 - это еще не доступно в студии Xamarin или как? Я имею в виду, что я не могу ссылаться на Timer, его нет в пространстве имен System.Threading в моем PCL.   -  person Aleksei Petrenko    schedule 25.03.2014
comment
Я бы посоветовал не использовать ответ, который просто подключает таймер .NET 4.0, а вместо этого создавать свой собственный класс таймера с помощью Task.Delay, например, в stackoverflow.com/a/21095323/957673. Это полностью устраняет проблему наличия ссылки на System.Threading.Timer.   -  person chkimes    schedule 25.03.2014
comment
Мне нравится класс Timer, я использовал его во многих проектах, и он тестировал миллионы раз. Я не хочу реализовывать свой собственный класс для такой простой вещи. Но если ничего не останется, может, придется)   -  person Aleksei Petrenko    schedule 25.03.2014


Ответы (3)


Вот полная повторная реализация класса Timer, который временно исчез в Профиле 78, с использованием асинхронных задач:

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;
            }
        }
    }
}
person Daniel Henry    schedule 01.07.2015

Вам нужно добавить привязку к app.config? Мне пришлось сделать что-то подобное для HttpClient, когда я добавил проект WP8.

<?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>
person valdetero    schedule 24.03.2014
comment
Я пробовал сделать что-то подобное, но это не помогло. Очевидно, у меня проблемы с mscorlib.dll. Их два - один из 4.5 PCL и один из 4.0 PCL. Мне нужно выяснить, как устранить двусмысленность. - person Aleksei Petrenko; 25.03.2014

Вместо того, чтобы помещать реализацию оболочки Timer в отдельный проект .net 4.0, я решил эту проблему другим способом:

Я создал интерфейс ITimerWrapper в основном проекте и поместил отдельные реализации в проекты Droid и WinPhone. Затем я использую IoC для настройки необходимой реализации при запуске приложения.

Между различными версиями таймера PCL, использующими этот метод, нет конфликта.

Еще одним преимуществом является то, что теперь я могу использовать DispatcherTimers в проекте WinPhone - я мог использовать System.Threading.Timers только тогда, когда делился кодом между Android и WinPhone.

person Ben Gladman    schedule 02.04.2014
comment
Мне нравится это решение :) - person Aleksei Petrenko; 30.03.2015