// 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;
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()
{
if (handler == null) throw new ArgumentNullException(nameof(handler));
_handler = 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();
}
}