WPF 添加一个不受UI线程影响的Loading

在加载时间长的页面中一般会有Loading,但是如果加载时间中有一部分占用UI线程(一般都要处理把耗时的逻辑放在工作线程,最后赋值的地方在UI线程),Loading就会出现卡顿的效果。

所以使用了后台线程加载Loading,占用UI线程时并不会影响Loading,示例如下:

<Window x:Class="BackGroundLoading.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BackGroundLoading"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <Button Click="ButtonBase_OnClick"></Button>
        <Grid x:Name="LoadingControl" Grid.Column="1" Background="Transparent" VerticalAlignment="Center">
            <local:DispatcherContainer x:Name="Host" Width="36" Height="36"></local:DispatcherContainer>
        </Grid>
    </Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace BackGroundLoading
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private bool loading = false;
        private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            loading = !loading;
            await SetLoadingControlAsync(loading);
        }

        /// <summary>
        /// 设置后台Loading
        /// </summary>
        /// <param name="visibility"></param>
        /// <returns></returns>
        private async Task SetLoadingControlAsync(bool visibility)
        {
            if (Host == null) return;
            if (visibility)
            {
                await Host?.Show<LoadingControl>();
            }
            else
            {
                await Host?.Hide();
            }
        }
    }
}
<UserControl x:Class="BackGroundLoading.LoadingControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d">
    <Grid>
        <Viewbox HorizontalAlignment="Center" VerticalAlignment="Center">
            <Grid x:Name="LayoutRoot" Background="Transparent"
                  HorizontalAlignment="Center" VerticalAlignment="Center">
                <Image x:Name="Image" RenderTransformOrigin="0.5,0.5" 
                       Source="{StaticResource Image.Operate.SaveFile.Loading}">
                    <Image.RenderTransform>
                        <RotateTransform x:Name="SpinnerRotate" Angle="0" />
                    </Image.RenderTransform>
                </Image>
            </Grid>
        </Viewbox>
    </Grid>
</UserControl>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Threading;

namespace BackGroundLoading
{
    /// <summary>
    /// LoadingControl.xaml 的交互逻辑
    /// </summary>
    public partial class LoadingControl : UserControl
    {
        public LoadingControl()
        {
            InitializeComponent();
            this.IsVisibleChanged += HandleVisibleChanged;
            animationTimer = new DispatcherTimer(DispatcherPriority.ContextIdle, Dispatcher);
            animationTimer.Interval = TimeSpan.FromMilliseconds(75);
        }
        private readonly DispatcherTimer animationTimer;
        private void Start()
        {
            animationTimer.Tick += HandleAnimationTick;
            animationTimer.Start();
        }

        private void Stop()
        {
            animationTimer.Stop();
            animationTimer.Tick -= HandleAnimationTick;
        }
        private void HandleVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            bool isVisible = (bool)e.NewValue;

            if (isVisible)
                Start();
            else
                Stop();
        }
        private void HandleAnimationTick(object sender, EventArgs e)
        {
            SpinnerRotate.Angle = (SpinnerRotate.Angle + 36) % 360;
        }
    }
}
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Threading;

namespace BackGroundLoading
{
    public sealed class DispatcherContainer : FrameworkElement
    {
        public DispatcherContainer()
        {
            _hostVisual = new HostVisual();
        }

        private readonly HostVisual _hostVisual;
        private VisualTargetPresentationSource _targetSource;

        #region Child

        private bool _isUpdatingChild;
        private UIElement _child;
        internal UIElement Child => _child;

        /// <summary>
        /// 设置子项
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dispatcher"></param>
        /// <returns></returns>
        public async Task SetChildAsync<T>(Dispatcher dispatcher = null)
            where T : UIElement, new()
        {
            await SetChildAsync(() => new T(), dispatcher);
        }

        public async Task SetChildAsync<T>(Func<T> @new, Dispatcher dispatcher = null)
            where T : UIElement
        {
            dispatcher ??= UIDispatcher.RunNew($"{typeof(T).Name}");
            var child =  dispatcher.Invoke(@new);
            await SetChildAsync(child);
        }

        public async Task SetChildPropertyAsync (Action action) 
        {
            var visualTarget = _targetSource;
            if (visualTarget != null)
            {
                await visualTarget.Dispatcher.InvokeAsync(action);
            }
        }

        public async Task SetChildAsync(UIElement value)
        {
            if (_isUpdatingChild)
            {
                throw new InvalidOperationException("Child property should not be set during Child updating.");
            }

            _isUpdatingChild = true;
            try
            {
                await SetChildAsync();
            }
            finally
            {
                _isUpdatingChild = false;
            }

            async Task SetChildAsync()
            {
                var oldChild = _child;
                var visualTarget = _targetSource;

                if (Equals(oldChild, value))
                    return;

                _targetSource = null;
                if (visualTarget != null)
                {
                    RemoveVisualChild(oldChild);
                    await visualTarget.Dispatcher.InvokeAsync(visualTarget.Dispose);
                }

                _child = value;

                if (value == null)
                {
                    _targetSource = null;
                }
                else
                {
                    await value.Dispatcher.InvokeAsync(() =>
                    {
                        _targetSource = new VisualTargetPresentationSource(_hostVisual)
                        {
                            RootVisual = value,
                        };
                    });
                    AddVisualChild(_hostVisual);
                }
                InvalidateMeasure();
            }
        }

        /// <summary>
        /// 显示
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="new"></param>
        /// <param name="dispatcher"></param>
        /// <returns></returns>
        public async Task Show<T>(Dispatcher dispatcher = null)
            where T :UIElement, new()
        {
            if (Child != null)
            {
                var visualTarget = _targetSource;
                if (visualTarget != null)
                {
                    await visualTarget.Dispatcher.InvokeAsync(() => Child.Visibility = Visibility.Visible);
                    return;
                }
            }
            await SetChildAsync(() => new T(), dispatcher);
        }

        /// <summary>
        /// 隐藏
        /// </summary>
        /// <returns></returns>
        public async Task Hide()
        {
            if (Child == null )
            {
                throw new InvalidOperationException("Non existent");
            }
            var visualTarget = _targetSource;
            if (visualTarget != null)
            {
                await visualTarget.Dispatcher.InvokeAsync(()=>Child.Visibility = Visibility.Collapsed);
            }
        }

        #endregion

        #region Tree & Layout

        protected override Visual GetVisualChild(int index)
        {
            if (index != 0)
                throw new ArgumentOutOfRangeException(nameof(index));
            return _hostVisual;
        }

        protected override int VisualChildrenCount => _child != null ? 1 : 0;

        protected override Size MeasureOverride(Size availableSize)
        {
            var child = _child;
            if (child == null)
                return default(Size);

            child.Dispatcher.InvokeAsync(
                () => child.Measure(availableSize),
                DispatcherPriority.Loaded);

            return default(Size);
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            var child = _child;
            if (child == null)
                return finalSize;

            child.Dispatcher.InvokeAsync(
                () => child.Arrange(new Rect(finalSize)),
                DispatcherPriority.Loaded);

            return finalSize;
        }

        #endregion

        #region HitTest

        protected override HitTestResult HitTestCore(PointHitTestParameters htp)
        {
            var child = _child;

            var element = child?.Dispatcher.Invoke(() =>
            {
                double offsetX = 0d, offsetY = 0d;
                if (child is FrameworkElement fe)
                {
                    offsetX = fe.Margin.Left;
                    offsetY = fe.Margin.Top;
                }
                return _child?.InputHitTest(new Point(htp.HitPoint.X - offsetX, htp.HitPoint.Y - offsetY));
            }, DispatcherPriority.Normal);
            if (element == null)
            {
                return null;
            }

            return new PointHitTestResult(this, htp.HitPoint);
        }

        #endregion
    }
}
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;

namespace BackGroundLoading
{
    public class VisualTargetPresentationSource : PresentationSource, IDisposable
    {
        public VisualTargetPresentationSource(HostVisual hostVisual)
        {
            _visualTarget = new VisualTarget(hostVisual);
        }

        public override Visual RootVisual
        {
            get => _visualTarget.RootVisual;
            set
            {
                if (IsDisposed)
                {
                    throw new ObjectDisposedException("VisualTarget");
                }

                var oldRoot = _visualTarget.RootVisual;
                _visualTarget.RootVisual = value;

                if (oldRoot is FrameworkElement oldRootFe)
                {
                    oldRootFe.SizeChanged -= root_SizeChanged;
                }
                if (value is FrameworkElement rootFe)
                {
                    rootFe.SizeChanged += root_SizeChanged;
                    rootFe.DataContext = _dataContext;

                    if (_propertyName != null)
                    {
                        var myBinding = new Binding(_propertyName)
                        {
                            Source = _dataContext
                        };
                        rootFe.SetBinding(TextBlock.TextProperty, myBinding);
                    }
                }

                //告诉PresentationSource根可视化改变。这引发了一系列的事情,比如已加载事件。
                RootChanged(oldRoot, value);

                //启动布局
                if (value is UIElement rootElement)
                {
                    rootElement.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
                    rootElement.Arrange(new Rect(rootElement.DesiredSize));
                }
            }
        }

        public object DataContext
        {
            get => _dataContext;
            set
            {
                if (IsDisposed)
                {
                    throw new ObjectDisposedException("VisualTarget");
                }

                if (_dataContext == value)
                {
                    return;
                }

                _dataContext = value;
                if (_visualTarget.RootVisual is FrameworkElement rootElement)
                {
                    rootElement.DataContext = _dataContext;
                }
            }
        }

        public string PropertyName
        {
            get => _propertyName;
            set
            {
                if (IsDisposed)
                {
                    throw new ObjectDisposedException("VisualTarget");
                }

                _propertyName = value;

                if (_visualTarget.RootVisual is TextBlock rootElement)
                {
                    if (!rootElement.CheckAccess())
                    {
                        throw new InvalidOperationException("What?");
                    }

                    var myBinding = new Binding(_propertyName)
                    {
                        Source = _dataContext
                    };
                    rootElement.SetBinding(TextBlock.TextProperty, myBinding);
                }
            }
        }

        public event SizeChangedEventHandler SizeChanged;

        public override bool IsDisposed => _isDisposed;

        protected override CompositionTarget GetCompositionTargetCore()
        {
            return _visualTarget;
        }

        private void root_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (IsDisposed)
            {
                return;
            }

            SizeChanged?.Invoke(this, e);
        }

        private readonly VisualTarget _visualTarget;
        private object _dataContext;
        private string _propertyName;
        private bool _isDisposed;

        public void Dispose()
        {
            _visualTarget?.Dispose();
            _isDisposed = true;
        }
    }
}
using System;
using System.Runtime.ExceptionServices;
using System.Threading;
using System.Windows.Threading;

namespace BackGroundLoading
{
    public static class UIDispatcher
    {
        /// <summary>
        /// 创建一个可以运行 <see cref="Dispatcher"/> 的后台 UI 线程,并返回这个线程的调度器 <see cref="Dispatcher"/>/// </summary>
        /// <param name="name">线程的名称,如果不指定,将使用 “BackgroundUI”。</param>
        /// <returns>一个可以异步等待的 <see cref="Dispatcher"/></returns>
        public static DispatcherAsyncOperation<Dispatcher> RunNewAsync(string name = null)
        {
            // 创建一个可等待的异步操作。
            var awaiter = DispatcherAsyncOperation<Dispatcher>.Create(out var reportResult);

            // 记录原线程关联的 Dispatcher,以便在意外时报告异常。
            var originDispatcher = Dispatcher.CurrentDispatcher;

            // 创建后台线程。
            var thread = new Thread(() =>
            {
                try
                {
                    // 获取关联此后台线程的 Dispatcher。
                    var dispatcher = Dispatcher.CurrentDispatcher;

                    // 设置此线程的 SynchronizationContext,以便此线程上 await 之后能够返回此线程。
                    SynchronizationContext.SetSynchronizationContext(
                        new DispatcherSynchronizationContext(dispatcher));

                    // 报告 Dispatcher 已创建完毕,使用 await 异步等待 Dispatcher 创建的地方可以继续执行了。
                    reportResult(dispatcher, null);
                }
                catch (Exception ex)
                {
                    // 报告创建过程中发生的异常。
                    // 不需要担心其内部发生的异常,因为会被异步状态机捕获后重新在原线程上抛出。
                    reportResult(null, ex);
                }

                // 此线程的以下代码将脱离异步状态机的控制,需要自己处理异常。
                try
                {
                    // 启动 Dispatcher,开始此线程上消息的调度。
                    Dispatcher.Run();
                }
                catch (Exception ex)
                {
                    // 如果新的 Dispatcher 线程上出现了未处理的异常,则将其抛到原调用线程上。
                    originDispatcher.InvokeAsync(() => ExceptionDispatchInfo.Capture(ex).Throw());
                }
            })
            {
                Name = name ?? "BackgroundUI",
                IsBackground = true,
            };
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            return awaiter;
        }

        /// <summary>
        /// 创建一个可以运行 <see cref="Dispatcher"/> 的后台 UI 线程,并返回这个线程的调度器 <see cref="Dispatcher"/>/// </summary>
        /// <param name="name">线程的名称,如果不指定,将使用 “BackgroundUI”。</param>
        /// <returns>后台线程创建并启动的 <see cref="Dispatcher"/></returns>
        public static Dispatcher RunNew(string name = null)
        {
            var resetEvent = new AutoResetEvent(false);

            // 记录原线程关联的 Dispatcher,以便在意外时报告异常。
            var originDispatcher = Dispatcher.CurrentDispatcher;
            Exception innerException = null;
            Dispatcher dispatcher = null;

            // 创建后台线程。
            var thread = new Thread(() =>
            {
                try
                {
                    // 获取关联此后台线程的 Dispatcher。
                    dispatcher = Dispatcher.CurrentDispatcher;

                    // 设置此线程的 SynchronizationContext,以便此线程上 await 之后能够返回此线程。
                    SynchronizationContext.SetSynchronizationContext(
                        new DispatcherSynchronizationContext(dispatcher));

                    // 报告 Dispatcher 已创建完毕,使用 ResetEvent 同步等待 Dispatcher 创建的地方可以继续执行了。
                    resetEvent.Set();
                }
                catch (Exception ex)
                {
                    // 报告创建过程中发生的异常。
                    innerException = ex;
                    resetEvent.Set();
                }

                // 此线程的以下代码将脱离异步状态机的控制,需要自己处理异常。
                try
                {
                    // 启动 Dispatcher,开始此线程上消息的调度。
                    Dispatcher.Run();
                }
                catch (Exception ex)
                {
                    // 如果新的 Dispatcher 线程上出现了未处理的异常,则将其抛到原调用线程上。
                    originDispatcher.InvokeAsync(() => ExceptionDispatchInfo.Capture(ex).Throw());
                }
            })
            {
                Name = name ?? "BackgroundUI",
                IsBackground = true,
            };
            thread.SetApartmentState(ApartmentState.STA);
            thread.Start();
            resetEvent.WaitOne();
            resetEvent.Dispose();
            resetEvent = null;
            if (innerException != null)
            {
                ExceptionDispatchInfo.Capture(innerException).Throw();
            }
            return dispatcher;
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace BackGroundLoading
{
    /// <summary>
    /// 表示可以等待一个主要运行在 UI 线程的异步操作。
    /// </summary>
    /// <typeparam name="T">异步等待 UI 操作结束后的返回值类型。</typeparam>
    public class DispatcherAsyncOperation<T> : DispatcherObject,
        IAwaitable<DispatcherAsyncOperation<T>, T>, IAwaiter<T>
    {
        /// <summary>
        /// 创建 <see cref="DispatcherAsyncOperation{T}"/> 的新实例。
        /// </summary>
        private DispatcherAsyncOperation()
        {
        }

        /// <summary>
        /// 获取一个可用于 await 关键字异步等待的异步等待对象。
        /// 此方法会被编译器自动调用。
        /// </summary>
        /// <returns>返回自身,用于异步等待返回值。</returns>
        public DispatcherAsyncOperation<T> GetAwaiter()
        {
            return this;
        }

        /// <summary>
        /// 获取一个状态,该状态表示正在异步等待的操作已经完成(成功完成或发生了异常)。
        /// 此状态会被编译器自动调用。
        /// </summary>
        public bool IsCompleted { get; private set; }

        /// <summary>
        /// 获取此异步等待操作的返回值。
        ///<see cref="System.Threading.Tasks.Task{TResult}"/> 不同的是,
        /// 如果操作没有完成或发生了异常,此实例会返回 <typeparamref name="T"/> 的默认值,
        /// 而不是阻塞线程直至任务完成。
        /// </summary>
        public T Result { get; private set; }

        /// <summary>
        /// 获取此异步等待操作的返回值,此方法会被编译器在 await 结束时自动调用以获取返回值。
        ///<see cref="System.Threading.Tasks.Task{TResult}"/> 不同的是,
        /// 如果操作没有完成,此实例会返回 <typeparamref name="T"/> 的默认值,而不是阻塞线程直至任务完成。
        /// 但是,如果异步操作中发生了异常,调用此方法会抛出这个异常。
        /// </summary>
        /// <returns>
        /// 异步操作的返回值。
        /// </returns>
        public T GetResult()
        {
            if (_exception != null)
            {
                ExceptionDispatchInfo.Capture(_exception).Throw();
            }
            return Result;
        }

        /// <summary>
        /// 使用 Builder 模式配置此异步操作执行完后,后续任务执行采用的优先级。
        /// 不配置时,使用的是 <see cref="DispatcherPriority.Normal"/>/// </summary>
        /// <param name="priority">使用 <see cref="Dispatcher"/> 调度的后续任务的优先级。</param>
        /// <returns>实例自身。</returns>
        public DispatcherAsyncOperation<T> ConfigurePriority(DispatcherPriority priority)
        {
            _priority = priority;
            return this;
        }

        /// <summary>
        /// 当使用此类型执行异步任务的方法执行完毕后,编译器会自动调用此方法。
        /// 也就是说,此方法会在调用方所在的线程执行,用于通知调用方所在线程的代码已经执行完毕,请求执行 await 后续任务。
        /// 在此类型中,后续任务是通过 <see cref="Dispatcher.InvokeAsync(Action, DispatcherPriority)"/> 来执行的。
        /// </summary>
        /// <param name="continuation">
        /// 被异步任务状态机包装的后续任务。当执行时,会让状态机继续往下走一步。
        /// </param>
        public void OnCompleted(Action continuation)
        {
            if (IsCompleted)
            {
                // 如果 await 开始时任务已经执行完成,则直接执行 await 后面的代码。
                // 注意,即便 _continuation 有值,也无需关心,因为报告结束的时候就会将其执行。
                continuation?.Invoke();
            }
            else
            {
                // 当使用多个 await 关键字等待此同一个 awaitable 实例时,此 OnCompleted 方法会被多次执行。
                // 当任务真正结束后,需要将这些所有的 await 后面的代码都执行。
                _continuation += continuation;
            }
        }

        /// <summary>
        /// 调用此方法以报告任务结束,并指定返回值和异步任务中的异常。
        /// 当使用 <see cref="Create"/> 静态方法创建此类型的实例后,调用方可以通过方法参数中传出的委托来调用此方法。
        /// </summary>
        /// <param name="result">异步返回值。</param>
        /// <param name="exception">异步操作中的异常。</param>
        private void ReportResult(T result, Exception exception)
        {
            Result = result;
            _exception = exception;
            IsCompleted = true;

            // _continuation 可能为 null,说明任务已经执行完毕,但没有任何一处 await 了这个任务。
            if (_continuation != null)
            {
                // 无论此方法执行时所在线程关联的 Dispatcher 是否等于此类型创建时的 Dispatcher;
                // 都 Invoke 到创建时的 Dispatcher 上,以便对当前执行上下文造成影响在不同线程执行下都一致(如异常)。
                Dispatcher.InvokeAsync(_continuation, _priority);
            }
        }

        /// <summary>
        /// 临时保存 await 后后续任务的包装,用于报告任务完成后能够继续执行。
        /// </summary>
        private Action _continuation;

        /// <summary>
        /// 临时保存异步任务执行过程中发生的异常。它会在异步等待结束后抛出,以报告异步执行过程中发生的错误。
        /// </summary>
        private Exception _exception;

        /// <summary>
        /// 储存恢复 await 后续任务时需要使用的优先级。
        /// </summary>
        private DispatcherPriority _priority = DispatcherPriority.Normal;

        /// <summary>
        /// 创建 <see cref="DispatcherAsyncOperation{T}"/> 的新实例,并得到一个可以用于报告操作执行完毕的委托。
        /// </summary>
        /// <param name="reportResult">一个委托。调用此委托可以报告任务已经执行完毕,并给定返回值和异常信息。</param>
        /// <returns>
        /// 创建好的 <see cref="DispatcherAsyncOperation{T}"/> 的新实例,将此返回值作为方法的返回值可以让方法支持 await 异步等待。
        /// </returns>
        public static DispatcherAsyncOperation<T> Create(out Action<T, Exception> reportResult)
        {
            var asyncOperation = new DispatcherAsyncOperation<T>();
            reportResult = asyncOperation.ReportResult;
            return asyncOperation;
        }
    }
}
using System.Runtime.CompilerServices;

namespace BackGroundLoading
{
    /// <summary>
    /// 表示一个可等待对象,如果一个方法返回此类型的实例,则此方法可以使用 `await` 异步等待。
    /// </summary>
    /// <typeparam name="TAwaiter">用于给 await 确定返回时机的 IAwaiter 的实例。</typeparam>
    public interface IAwaitable<out TAwaiter> where TAwaiter : IAwaiter
    {
        /// <summary>
        /// 获取一个可用于 await 关键字异步等待的异步等待对象。
        /// 此方法会被编译器自动调用。
        /// </summary>
        TAwaiter GetAwaiter();
    }

    /// <summary>
    /// 表示一个包含返回值的可等待对象,如果一个方法返回此类型的实例,则此方法可以使用 `await` 异步等待返回值。
    /// </summary>
    /// <typeparam name="TAwaiter">用于给 await 确定返回时机的 IAwaiter{<typeparamref name="TResult"/>} 的实例。</typeparam>
    /// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
    public interface IAwaitable<out TAwaiter, out TResult> where TAwaiter : IAwaiter<TResult>
    {
        /// <summary>
        /// 获取一个可用于 await 关键字异步等待的异步等待对象。
        /// 此方法会被编译器自动调用。
        /// </summary>
        TAwaiter GetAwaiter();
    }

    /// <summary>
    /// 用于给 await 确定异步返回的时机。
    /// </summary>
    public interface IAwaiter : INotifyCompletion
    {
        /// <summary>
        /// 获取一个状态,该状态表示正在异步等待的操作已经完成(成功完成或发生了异常);此状态会被编译器自动调用。
        /// 在实现中,为了达到各种效果,可以灵活应用其值:可以始终为 true,或者始终为 false。
        /// </summary>
        bool IsCompleted { get; }

        /// <summary>
        /// 此方法会被编译器在 await 结束时自动调用以获取返回状态(包括异常)。
        /// </summary>
        void GetResult();
    }

    /// <summary>
    /// 当执行关键代码(此代码中的错误可能给应用程序中的其他状态造成负面影响)时,
    /// 用于给 await 确定异步返回的时机。
    /// </summary>
    public interface ICriticalAwaiter : IAwaiter, ICriticalNotifyCompletion
    {
    }

    /// <summary>
    /// 用于给 await 确定异步返回的时机,并获取到返回值。
    /// </summary>
    /// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
    public interface IAwaiter<out TResult> : INotifyCompletion
    {
        /// <summary>
        /// 获取一个状态,该状态表示正在异步等待的操作已经完成(成功完成或发生了异常);此状态会被编译器自动调用。
        /// 在实现中,为了达到各种效果,可以灵活应用其值:可以始终为 true,或者始终为 false。
        /// </summary>
        bool IsCompleted { get; }

        /// <summary>
        /// 获取此异步等待操作的返回值,此方法会被编译器在 await 结束时自动调用以获取返回值(包括异常)。
        /// </summary>
        /// <returns>异步操作的返回值。</returns>
        TResult GetResult();
    }

    /// <summary>
    /// 当执行关键代码(此代码中的错误可能给应用程序中的其他状态造成负面影响)时,
    /// 用于给 await 确定异步返回的时机,并获取到返回值。
    /// </summary>
    /// <typeparam name="TResult">异步返回的返回值类型。</typeparam>
    public interface ICriticalAwaiter<out TResult> : IAwaiter<TResult>, ICriticalNotifyCompletion
    {
    }
}

 

posted @ 2023-06-08 16:15  log9527  阅读(388)  评论(0编辑  收藏  举报