// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Threading; using System.Diagnostics; namespace System { /// /// Provides an IProgress{T} that invokes callbacks for each reported progress value. /// /// Specifies the type of the progress report value. /// /// Any handler provided to the constructor or event handlers registered with /// the event are invoked through a /// instance captured /// when the instance is constructed. If there is no current SynchronizationContext /// at the time of construction, the callbacks will be invoked on the ThreadPool. /// public class Progress : IProgress { /// The synchronization context captured upon construction. This will never be null. private readonly SynchronizationContext _synchronizationContext; /// The handler specified to the constructor. This may be null. private readonly Action? _handler; /// A cached delegate used to post invocation to the synchronization context. private readonly SendOrPostCallback _invokeHandlers; /// Initializes the . public Progress() { // Capture the current synchronization context. // If there is no current context, we use a default instance targeting the ThreadPool. _synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext; Debug.Assert(_synchronizationContext != null); _invokeHandlers = new SendOrPostCallback(InvokeHandlers); } /// Initializes the with the specified callback. /// /// A handler to invoke for each reported progress value. This handler will be invoked /// in addition to any delegates registered with the event. /// Depending on the instance captured by /// the at construction, it's possible that this handler instance /// could be invoked concurrently with itself. /// /// The is null (Nothing in Visual Basic). public Progress(Action handler) : this() { _handler = handler ?? throw new ArgumentNullException(nameof(handler)); } /// Raised for each reported progress value. /// /// Handlers registered with this event will be invoked on the /// captured when the instance was constructed. /// public event EventHandler? ProgressChanged; /// Reports a progress change. /// The value of the updated progress. protected virtual void OnReport(T value) { // If there's no handler, don't bother going through the sync context. // Inside the callback, we'll need to check again, in case // an event handler is removed between now and then. Action? handler = _handler; EventHandler? changedEvent = ProgressChanged; if (handler != null || changedEvent != null) { // Post the processing to the sync context. // (If T is a value type, it will get boxed here.) _synchronizationContext.Post(_invokeHandlers, value); } } /// Reports a progress change. /// The value of the updated progress. void IProgress.Report(T value) { OnReport(value); } /// Invokes the action and event callbacks. /// The progress value. private void InvokeHandlers(object? state) { T value = (T)state!; Action? handler = _handler; EventHandler? changedEvent = ProgressChanged; if (handler != null) handler(value); if (changedEvent != null) changedEvent(this, value); } } /// Holds static values for . /// This avoids one static instance per type T. internal static class ProgressStatics { /// A default synchronization context that targets the ThreadPool. internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); } }