Skip to content

Instantly share code, notes, and snippets.

@myd7349
Forked from martinskuta/AsynCommand.cs
Created April 7, 2026 09:03
Show Gist options
  • Select an option

  • Save myd7349/c0bab416bdfbb3dc32b275d1918ed3e1 to your computer and use it in GitHub Desktop.

Select an option

Save myd7349/c0bab416bdfbb3dc32b275d1918ed3e1 to your computer and use it in GitHub Desktop.
Implementation of AsyncCommand for WPF
#region Usings
using System;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Input;
#endregion
namespace AsyncCommandGist
{
/// <summary>
/// Interface for asynchronous commands that allow to await the command's execution.
/// </summary>
public interface IAsyncCommand : ICommand
{
/// <summary>
/// Executes the task asynchronously.
/// </summary>
/// <returns></returns>
Task ExecuteAsync(object parameter = null);
}
/// <summary>
/// A command that executes an asynchronous delegate.
/// </summary>
public class AsyncCommand : IAsyncCommand
{
private readonly WeakFunc<bool> _canExecute;
/// <summary>
/// The asynchronous delegate to execute.
/// </summary>
private readonly WeakFunc<Task> _commandTask;
/// <summary>
/// Creates a command that executes the specified asynchronous delegate.
/// </summary>
/// <param name="commandTask">The asynchronous delegate to execute. May not be <c>null</c>.</param>
/// <param name="canExecute">The function that returns bool indicating if the command can or cannot execute.</param>
public AsyncCommand(Func<Task> commandTask, Func<bool> canExecute = null)
{
if (commandTask == null) throw new ArgumentNullException(nameof(commandTask));
_commandTask = WeakFunc<Task>.Create(null, commandTask);
if (canExecute != null) _canExecute = WeakFunc<bool>.Create(null, canExecute);
}
#region IAsyncCommand implementation
/// <summary>
/// Returns a value indicating whether the command may be executed. This implementation always returns <c>true</c>.
/// </summary>
/// <param name="parameter">The parameter for this command.</param>
public virtual bool CanExecute(object parameter = null)
{
return _canExecute?.Execute() ?? true;
}
async void ICommand.Execute(object parameter)
{
await ExecuteAsync(parameter);
}
/// <summary>
/// Executes the command asynchronously. The parameter if passed is always ignored.
/// </summary>
/// <param name="parameter">The parameter is always ignored.</param>
public Task ExecuteAsync(object parameter = null)
{
return _commandTask.Execute();
}
/// <summary>
/// An event indicating that the return value of <see cref="CanExecute"/> may have changed.
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#endregion
}
/// <summary>
/// Class that stores a <see cref="Func{TResult}"/> without causing a hard reference, so the target can be garbage collected.
/// </summary>
/// <typeparam name="T">Type of the result of the func.</typeparam>
public abstract class WeakFunc<T>
{
/// <summary>
/// Gets or sets the owner of the reference, which might be different from actual target of the func in case of anonymous delegate eg.
/// </summary>
/// <value>
/// The owner reference.
/// </value>
protected WeakReference OwnerReference { get; set; }
/// <summary>
/// Gets a value indicating whether the <see cref="Target"/> is still alive and was not garbage collected.
/// </summary>
public abstract bool IsAlive { get; }
/// <summary>
/// Gets the target of the func or null if it was already garbage collected.
/// </summary>
public object Target => OwnerReference?.Target;
protected WeakFunc(object target)
{
if (target == null) return;
OwnerReference = new WeakReference(target);
}
/// <summary>
/// Executes the actual func
/// </summary>
/// <returns></returns>
public abstract T Execute();
/// <summary>
/// Creates a specific implementation of WeakFunc.
/// </summary>
/// <typeparam name="TResult">The type of the result.</typeparam>
/// <param name="target">The target.</param>
/// <param name="func">The function.</param>
/// <returns></returns>
public static WeakFunc<TResult> Create<TResult>(object target, Func<TResult> func)
{
if (func.Method.IsStatic) return new WeakStaticFunc<TResult>(target, func);
return new WeakInstanceFunc<TResult>(target, func);
}
#region WeakInstanceFunc
/// <summary>
/// Implementation of a weak func that is target an actual instance (eg. is not static)
/// </summary>
/// <typeparam name="T1"></typeparam>
private class WeakInstanceFunc<T1> : WeakFunc<T1>
{
/// <summary>
/// The actual weak reference to a target of the func, which might be different from the actual target in case of anonymous lambda for example.
/// </summary>
/// <value>
/// The function reference.
/// </value>
private WeakReference FuncReference { get; }
/// <summary>
/// Gets or sets the <see cref="T:System.Reflection.MethodInfo"/> corresponding to this fun's method passed in the constructor.
/// </summary>
private MethodInfo Method { get; }
public override bool IsAlive => OwnerReference?.IsAlive ?? false;
public WeakInstanceFunc(object target, Func<T1> func) : base(target)
{
if (func == null) throw new ArgumentNullException(nameof(func));
if (func.Method.IsStatic)
throw new ArgumentException(
"The func cannot be static, this implementation of weak func requires instace method.");
Method = func.Method;
FuncReference = new WeakReference(func.Target);
}
public override T1 Execute()
{
var funcTarget = FuncReference.Target;
if (funcTarget == null) return default(T1);
return (T1)Method.Invoke(funcTarget, null);
}
}
#endregion
#region WeakStaticFunc
/// <summary>
/// Special case when the func passed in is static, in this case the func is weak by definition, because
/// it doesn't hold reference to anything and is always alive. The only weak reference is the Target.
/// </summary>
/// <typeparam name="T2"></typeparam>
private class WeakStaticFunc<T2> : WeakFunc<T2>
{
private readonly Func<T2> _staticFunc;
public override bool IsAlive => true;
internal WeakStaticFunc(object target, Func<T2> func) : base(target)
{
if (func == null) throw new ArgumentNullException(nameof(func));
if (!func.Method.IsStatic)
throw new ArgumentException("The func must be static to create weak static func.");
_staticFunc = func;
}
public override T2 Execute()
{
return _staticFunc();
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment