< Summary

Information
Class: MyNet.UI.Commands.AsyncRelayCommand
Assembly: MyNet.UI
File(s): https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.UI/Commands/AsyncRelayCommand.cs
Tag: 323_28699572109
Line coverage
92%
Covered lines: 12
Uncovered lines: 1
Coverable lines: 13
Total lines: 138
Line coverage: 92.3%
Branch coverage
92%
Covered branches: 13
Total branches: 14
Branch coverage: 92.8%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)83.33%66100%
CanExecute(...)100%44100%
ExecuteAsync()100%44100%
System-Windows-Input-ICommand-Execute()100%210%
RaiseCanExecuteChanged()100%11100%

File(s)

https://raw.githubusercontent.com/sandre58/MyNet/85372080fe102cd9ee155ceab49ae000e7f66103/src/MyNet.UI/Commands/AsyncRelayCommand.cs

#LineLine coverage
 1// -----------------------------------------------------------------------
 2// <copyright file="AsyncRelayCommand.cs" company="Stéphane ANDRE">
 3// Copyright (c) Stéphane ANDRE. All rights reserved.
 4// </copyright>
 5// -----------------------------------------------------------------------
 6
 7using System;
 8using System.Diagnostics.CodeAnalysis;
 9using System.Reactive.Concurrency;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using System.Windows.Input;
 13using MyNet.UI.Threading;
 14
 15namespace MyNet.UI.Commands;
 16
 17/// <summary>
 18/// An implementation of <see cref="IAsyncCommand"/> that relays its functionality to the provided asynchronous delegate
 19/// </summary>
 20/// <param name="execute">The asynchronous delegate to execute when the command is invoked.</param>
 21/// <param name="canExecute">An optional delegate to determine whether the command can execute.</param>
 22/// <param name="schedulerProvider">An optional scheduler provider to specify the UI thread scheduler.</param>
 23public sealed class AsyncRelayCommand(Func<Task> execute, Func<bool>? canExecute = null, ISchedulerProvider? schedulerPr
 24{
 35725    private readonly Func<Task> _execute = execute ?? throw new ArgumentNullException(nameof(execute));
 35726    private readonly IScheduler _uiScheduler = schedulerProvider?.Ui ?? CurrentThreadScheduler.Instance;
 27    private int _isExecuting;
 28
 29    /// <summary>
 30    /// Gets the event that is raised when the return value of the CanExecute method changes. This event should be raise
 31    /// </summary>
 32    public event EventHandler? CanExecuteChanged;
 33
 34    /// <summary>
 35    /// Determines whether the command can execute in its current state. The command can execute if it is not currently 
 36    /// </summary>
 37    /// <param name="parameter">The command parameter.</param>
 38    /// <returns><see langword="true"/> if the command can execute; otherwise, <see langword="false"/>.</returns>
 2739    public bool CanExecute(object? parameter) => Volatile.Read(ref _isExecuting) == 0 && (canExecute?.Invoke() ?? true);
 40
 41    /// <summary>
 42    /// Asynchronously executes the command. If the command cannot execute, this method does nothing. This method is thr
 43    /// </summary>
 44    /// <param name="parameter">The command parameter.</param>
 45    public async Task ExecuteAsync(object? parameter)
 46    {
 1547        if (!CanExecute(parameter) || Interlocked.CompareExchange(ref _isExecuting, 1, 0) != 0)
 348            return;
 49
 50        try
 51        {
 1252            RaiseCanExecuteChanged();
 53
 1254            await _execute().ConfigureAwait(false);
 1255        }
 56        finally
 57        {
 1258            Interlocked.Exchange(ref _isExecuting, 0);
 1259            RaiseCanExecuteChanged();
 60        }
 1561    }
 62
 63    /// <summary>
 64    /// Asynchronously executes the command. This method is required by the ICommand interface and simply calls the Exec
 65    /// </summary>
 66    /// <param name="parameter">The command parameter.</param>
 67    [SuppressMessage("ReSharper", "AsyncVoidMethod", Justification = "Required by ICommand interface.")]
 068    async void ICommand.Execute(object? parameter) => await ExecuteAsync(parameter).ConfigureAwait(false);
 69
 70    /// <summary>
 71    /// Raises the CanExecuteChanged event to indicate that the return value of the CanExecute method has changed. This 
 72    /// </summary>
 2773    public void RaiseCanExecuteChanged() => _uiScheduler.Schedule(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty)
 74}
 75
 76/// <summary>
 77/// An implementation of <see cref="IAsyncCommand"/> that relays its functionality to the provided asynchronous delegate
 78/// </summary>
 79/// <param name="execute">The asynchronous delegate to execute when the command is invoked.</param>
 80/// <param name="canExecute">The delegate that determines whether the command can execute.</param>
 81/// <param name="schedulerProvider">The scheduler provider for UI thread scheduling.</param>
 82/// <typeparam name="T">The type of the command parameter.</typeparam>
 83public sealed class AsyncRelayCommand<T>(Func<T?, Task> execute, Func<T?, bool>? canExecute = null, ISchedulerProvider? 
 84{
 85    private readonly Func<T?, Task> _execute = execute ?? throw new ArgumentNullException(nameof(execute));
 86    private readonly IScheduler _uiScheduler = schedulerProvider?.Ui ?? CurrentThreadScheduler.Instance;
 87    private int _isExecuting;
 88
 89    /// <summary>
 90    /// Gets the event that is raised when the return value of the CanExecute method changes. This event should be raise
 91    /// </summary>
 92    public event EventHandler? CanExecuteChanged;
 93
 94    /// <summary>
 95    /// Determines whether the command can execute in its current state. The command can execute if it is not currently 
 96    /// </summary>
 97    /// <param name="parameter">The command parameter.</param>
 98    /// <returns>True if the command can execute; otherwise, false.</returns>
 99    public bool CanExecute(object? parameter)
 100        => Volatile.Read(ref _isExecuting) == 0
 101            && (parameter is T or null)
 102            && (canExecute?.Invoke((T?)parameter) ?? true);
 103
 104    /// <summary>
 105    /// Asynchronously executes the command. If the command cannot execute, this method does nothing. This method is thr
 106    /// </summary>
 107    /// <param name="parameter">The command parameter.</param>
 108    /// <returns>A task that represents the asynchronous operation.</returns>
 109    public async Task ExecuteAsync(object? parameter)
 110    {
 111        if (!CanExecute(parameter) || Interlocked.CompareExchange(ref _isExecuting, 1, 0) != 0)
 112            return;
 113
 114        try
 115        {
 116            RaiseCanExecuteChanged();
 117            await _execute((T?)parameter).ConfigureAwait(false);
 118        }
 119        finally
 120        {
 121            Interlocked.Exchange(ref _isExecuting, 0);
 122            RaiseCanExecuteChanged();
 123        }
 124    }
 125
 126    /// <summary>
 127    /// Asynchronously executes the command. This method is required by the ICommand interface and simply calls the Exec
 128    /// </summary>
 129    /// <param name="parameter">The command parameter.</param>
 130    [SuppressMessage("ReSharper", "AsyncVoidMethod", Justification = "Required by ICommand interface.")]
 131    async void ICommand.Execute(object? parameter) => await ExecuteAsync(parameter).ConfigureAwait(false);
 132
 133    /// <summary>
 134    /// Raises the CanExecuteChanged event to indicate that the return value of the CanExecute method has changed. This 
 135    /// </summary>
 136    public void RaiseCanExecuteChanged() => _uiScheduler.Schedule(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty)
 137}
 138