【问题标题】:DispatcherScheduler missing from System.Reactive 5.0System.Reactive 5.0 中缺少 DispatcherScheduler
【发布时间】:2021-01-15 16:01:18
【问题描述】:

我正在尝试将我的 .net framework 4.7.2 项目从 System.Reactive 4.4 升级到 5.0。但是,似乎 DispatcherScheduler 和将 Dispatcher 转换为 IScheduler 的扩展方法在这两个版本之间都消失了。他们被淘汰了吗?移动到不同的包?

【问题讨论】:

    标签: .net wpf system.reactive


    【解决方案1】:

    在这篇文章中讨论过: https://github.com/dotnet/reactive/issues/1423

    基本上解决方案是 Target 框架:

    <TargetFramework>net5.0-windows10.0.19041</TargetFramework>
    

    这对我来说不是一个好的解决方案,所以我在我的项目中复制了旧的 DispatcherScheduler(使用 C#9):

    using System.Reactive.Disposables;
    using System.Threading;
    
    namespace System.Reactive.Concurrency
    {
        /// <summary>
        /// Represents an object that schedules units of work on a <see cref="System.Windows.Threading.Dispatcher"/>.
        /// </summary>
        /// <remarks>
        /// This scheduler type is typically used indirectly through the <see cref="Linq.DispatcherObservable.ObserveOnDispatcher{TSource}(IObservable{TSource})"/> and <see cref="Linq.DispatcherObservable.SubscribeOnDispatcher{TSource}(IObservable{TSource})"/> methods that use the Dispatcher on the calling thread.
        /// </remarks>
        public class DispatcherScheduler : LocalScheduler, ISchedulerPeriodic
        {
    
            /// <summary>
            /// Gets the scheduler that schedules work on the <see cref="System.Windows.Threading.Dispatcher"/> for the current thread.
            /// </summary>
            public static DispatcherScheduler Current
            {
                get
                {
                    var dispatcher = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread);
                    if (dispatcher == null)
                    {
                        throw new InvalidOperationException("NO_DISPATCHER_CURRENT_THREAD");
                    }
    
                    return new DispatcherScheduler(dispatcher);
                }
            }
    
            /// <summary>
            /// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/>.
            /// </summary>
            /// <param name="dispatcher"><see cref="DispatcherScheduler"/> to schedule work on.</param>
            /// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception>
            public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher)
            {
                Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
                Priority = System.Windows.Threading.DispatcherPriority.Normal;
    
            }
    
            /// <summary>
            /// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/> at the given priority.
            /// </summary>
            /// <param name="dispatcher"><see cref="DispatcherScheduler"/> to schedule work on.</param>
            /// <param name="priority">Priority at which units of work are scheduled.</param>
            /// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception>
            public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher, System.Windows.Threading.DispatcherPriority priority)
            {
                Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
                Priority = priority;
            }
    
            /// <summary>
            /// Gets the <see cref="System.Windows.Threading.Dispatcher"/> associated with the <see cref="DispatcherScheduler"/>.
            /// </summary>
            public System.Windows.Threading.Dispatcher Dispatcher { get; }
    
            /// <summary>
            /// Gets the priority at which work items will be dispatched.
            /// </summary>
            public System.Windows.Threading.DispatcherPriority Priority { get; }
    
            /// <summary>
            /// Schedules an action to be executed on the dispatcher.
            /// </summary>
            /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
            /// <param name="state">State passed to the action to be executed.</param>
            /// <param name="action">Action to be executed.</param>
            /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
            /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
            public override IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
            {
                if (action == null)
                {
                    throw new ArgumentNullException(nameof(action));
                }
    
                var d = new SingleAssignmentDisposable();
    
                Dispatcher.BeginInvoke(
                    new Action(() =>
                    {
                        if (!d.IsDisposed)
                        {
                            d.Disposable = action(this, state);
                        }
                    }),
                    Priority
                );
    
                return d;
            }
    
            /// <summary>
            /// Schedules an action to be executed after <paramref name="dueTime"/> on the dispatcher, using a <see cref="System.Windows.Threading.DispatcherTimer"/> object.
            /// </summary>
            /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
            /// <param name="state">State passed to the action to be executed.</param>
            /// <param name="action">Action to be executed.</param>
            /// <param name="dueTime">Relative time after which to execute the action.</param>
            /// <returns>The disposable object used to cancel the scheduled action (best effort).</returns>
            /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
            public override IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
            {
                if (action == null)
                {
                    throw new ArgumentNullException(nameof(action));
                }
    
                var dt = Scheduler.Normalize(dueTime);
                if (dt.Ticks == 0)
                {
                    return Schedule(state, action);
                }
    
                return ScheduleSlow(state, dt, action);
            }
    
            private IDisposable ScheduleSlow<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
            {
                var d = new MultipleAssignmentDisposable();
    
                var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher);
    
                timer.Tick += (s, e) =>
                {
                    var t = Interlocked.Exchange(ref timer, null);
                    if (t != null)
                    {
                        try
                        {
                            d.Disposable = action(this, state);
                        }
                        finally
                        {
                            t.Stop();
                            action = static (s, t) => Disposable.Empty;
                        }
                    }
                };
    
                timer.Interval = dueTime;
                timer.Start();
    
                d.Disposable = Disposable.Create(() =>
                {
                    var t = Interlocked.Exchange(ref timer, null);
                    if (t != null)
                    {
                        t.Stop();
                        action = static (s, t) => Disposable.Empty;
                    }
                });
    
                return d;
            }
    
            /// <summary>
            /// Schedules a periodic piece of work on the dispatcher, using a <see cref="System.Windows.Threading.DispatcherTimer"/> object.
            /// </summary>
            /// <typeparam name="TState">The type of the state passed to the scheduled action.</typeparam>
            /// <param name="state">Initial state passed to the action upon the first iteration.</param>
            /// <param name="period">Period for running the work periodically.</param>
            /// <param name="action">Action to be executed, potentially updating the state.</param>
            /// <returns>The disposable object used to cancel the scheduled recurring action (best effort).</returns>
            /// <exception cref="ArgumentNullException"><paramref name="action"/> is <c>null</c>.</exception>
            /// <exception cref="ArgumentOutOfRangeException"><paramref name="period"/> is less than <see cref="TimeSpan.Zero"/>.</exception>
            public IDisposable SchedulePeriodic<TState>(TState state, TimeSpan period, Func<TState, TState> action)
            {
                if (period < TimeSpan.Zero)
                {
                    throw new ArgumentOutOfRangeException(nameof(period));
                }
    
                if (action == null)
                {
                    throw new ArgumentNullException(nameof(action));
                }
    
                var timer = new System.Windows.Threading.DispatcherTimer(Priority, Dispatcher);
    
                var state1 = state;
    
                timer.Tick += (s, e) =>
                {
                    state1 = action(state1);
                };
    
                timer.Interval = period;
                timer.Start();
    
                return Disposable.Create(() =>
                {
                    var t = Interlocked.Exchange(ref timer, null);
                    if (t != null)
                    {
                        t.Stop();
                        action = static _ => _;
                    }
                });
            }
        }
    }
    

    尽管我的开发机器使用的是最新版本的 Windows 10,但它对我来说没有问题。

    【讨论】:

    • 我仍然对为什么包不能为这个类提供框架 4.7.1 感到困惑,但我想如果它是唯一的选择,这种方法是可行的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多