An approach towards verbose ReactiveUI command declarations

After having updated all the MVVM commands to follow the ReactiveUI 7 style as described in my previous blog post, there a few more things in the code I wasn’t fully content with: All the code lines for command declaration and initizalition are type-safe and quite verbose by now, which unfortunately does not make them more readable. For example, consider the following four command property declarations:

public ReactiveCommand<Unit, Unit> SimpleCommand { get; }
public ReactiveCommand<int, Unit> CommandWithParameter { get; }
public ReactiveCommand<Unit, bool> CommandWithResult { get; }
public ReactiveCommand<int, bool> AdvancedCommand { get; }

They all look pretty much the same, although represents different types of commands: Only some of them accept a parameter or return a result value, which (in my opinion) is an important difference which affects how to consume these commands. One of the reasons for bad readability is the obligatory use of the Reactive-internal Unit type, which acts as placeholder to be interpreted as nothing or void. (By the way, note that this actually needs to be System.Reactive.Unit if not including using System.Reactive. One reason for me disliking this type in general is that I’m using an additional dependency that also declares a type called Unit, so (to avoid mismatch, which had cost me hours of debugging time more than once) I’m in general using the full namespace everywhere – a practice that generates even longer lines of code, making the property declarations even less readable.)

To overcome all those little disadvantages, I came up with a solution that includes a few wrapper classes: The idea is to create explicit command classes for each type of command, and even explicitly name these after their purpose. For example, we can create a ResultReactiveCommand class as follows:

public class ResultReactiveCommand<TResult> : ReactiveCommand<Unit, TResult>
{
	protected internal ResultReactiveCommand(Func<Unit, IObservable<TResult>> execute,
		IObservable<bool> canExecute = null, IScheduler outputScheduler = null)
		: base(execute, canExecute ?? Observable.Return(true), outputScheduler ?? RxApp.MainThreadScheduler)
	{
	}

	public static ResultReactiveCommand<TResult> Create(Func<TResult> execute,
		IObservable<bool> canExecute = null, IScheduler outputScheduler = null)
	{
		if (execute == null)
			throw new ArgumentNullException(nameof(execute));

		Func<Unit, IObservable<TResult>> func = unit => Observable.Start(execute);
		return new ResultReactiveCommand<TResult>(func, canExecute, outputScheduler);
	}

	public static ResultReactiveCommand<TResult> CreateFromTask(Func<Task<TResult>> execute,
		IObservable<bool> canExecute = null, IScheduler outputScheduler = null)
	{
		if (execute == null)
			throw new ArgumentNullException(nameof(execute));

		Func<Unit, IObservable<TResult>> func = unit => Observable.StartAsync(execute);
		return new ResultReactiveCommand<TResult>(func, canExecute, outputScheduler);
	}
}

This code snippet might look confusing at first sight, but is actually not so difficult to understand. Note the following features:

  • The ResultReactiveCommand class has only one generic parameter (which represents the type of the result the command will generate) – the command does not accept a parameter, so there is no need to specify a TParam type parameter as in the original ReactiveCommand.
  • It is derived from the original ReactiveCommand class, so the only thing we need to overload is the command’s creation, for everything else the underlying ReactiveCommand-internal logic will be used. This also means our custom command can be used in combination with ReactiveCommand instances if desired, since under hood both represent the same type.
  • The former means: We obviously need to create our own constructor, however all it does is calling the underlying ReactiveCommand constructor (the only magic in the constructor method is the use of optional parameters, which are replaced by default values if null – this is necessary to be able to use our custom command without specifying canExecute condition and scheduler).
  • To mimic the original ReactiveCommand behavior and make it as easy as possible to use our custom command wrapper class, we define the same static Create methods that are well-known from ReactiveCommand (note that in this sample code snippet, I only included the Create and CreateFromTask methods because these are the only ones needed in my current project, but of course a CreateFromObservable method could be added in a similar way if necessary).
  • All these Create... methods do is initialize an anonymous method to execute the command’s business logic, and call the constructor (see above!) to instantiate the underlying ReactiveCommand!

In a similar way, a ParamReactiveCommand<TParam> that specifies a parameter type but no return value, and a SimpleReactiveCommand that does neither accept parameters not return a result could be implemented. Of course, it would also be possible to create a ParamResultReactiveCommand<TParam, TResult>, however I chose to simply use the original ReactiveCommand<TParam, TResult> for that purpose.

With this set of wrapper classes at hand, we can create shorter and more verbose command declarations through

  • using self-explanatory class names, and
  • omitting type parameters completely, instead of filling them with the unnecessary System.Reactive.Unit type,

as the following example snippet (a duplicate of the snippet presented at the beginning of this article) shows:

public SimpleReactiveCommand SimpleCommand { get; }
public ParamReactiveCommand<int> CommandWithParameter { get; }
public ResultReactiveCommand<bool> CommandWithResult { get; }
public ReactiveCommand<int, bool> AdvancedCommand { get; }

Also the usage of our custom command classes is rather easy since it does not differ much from the default ReactiveCommand:

public MyViewModel()
{
	SimpleCommand = SimpleReactiveCommand.Create(SimpleCommandImpl);
	CommandWithParameter = ParamReactiveCommand<int>.CreateFromTask(CommandWithParameterImpl);
	CommandWithResult = ResultReactiveCommand<bool>.CreateFromTask(CommandWithResult);
	AdvancedCommand = ReactiveCommand.CreateAsyncTask(AdvancedCommandImpl);
}