From 6ce52c70f7cfb5a2f867898db422b4a728662ae1 Mon Sep 17 00:00:00 2001 From: Alexey Golub Date: Mon, 16 Dec 2019 22:12:17 +0200 Subject: [PATCH] Use ValueTask --- .github/workflows/CD.yml | 2 +- .github/workflows/CI.yml | 2 +- CliFx.Benchmarks/Benchmark.cs | 6 ++--- CliFx.Benchmarks/CliFx.Benchmarks.csproj | 6 ++--- CliFx.Benchmarks/Commands/CliFxCommand.cs | 2 +- CliFx.Demo/CliFx.Demo.csproj | 6 ++--- CliFx.Demo/Commands/BookAddCommand.cs | 4 +-- CliFx.Demo/Commands/BookCommand.cs | 4 +-- CliFx.Demo/Commands/BookListCommand.cs | 4 +-- CliFx.Demo/Commands/BookRemoveCommand.cs | 4 +-- CliFx.Demo/Program.cs | 4 +-- CliFx.Tests/CliFx.Tests.csproj | 2 +- .../TestCommands/CancellableCommand.cs | 2 +- .../TestCommands/CommandExceptionCommand.cs | 2 +- CliFx.Tests/TestCommands/ConcatCommand.cs | 4 +-- CliFx.Tests/TestCommands/DivideCommand.cs | 4 +-- .../DuplicateOptionNamesCommand.cs | 2 +- .../DuplicateOptionShortNamesCommand.cs | 2 +- .../EnvironmentVariableCommand.cs | 2 +- ...onmentVariableWithMultipleValuesCommand.cs | 2 +- ...ariableWithoutCollectionPropertyCommand.cs | 2 +- CliFx.Tests/TestCommands/ExceptionCommand.cs | 2 +- .../TestCommands/HelloWorldDefaultCommand.cs | 4 +-- .../TestCommands/HelpDefaultCommand.cs | 2 +- CliFx.Tests/TestCommands/HelpNamedCommand.cs | 2 +- CliFx.Tests/TestCommands/HelpSubCommand.cs | 2 +- .../TestCommands/NonAnnotatedCommand.cs | 2 +- CliFx/CliApplication.cs | 6 ++--- CliFx/CliFx.csproj | 21 ++++++++------- CliFx/ICliApplication.cs | 2 +- CliFx/ICommand.cs | 2 +- Readme.md | 26 +++++++++---------- 32 files changed, 71 insertions(+), 68 deletions(-) diff --git a/.github/workflows/CD.yml b/.github/workflows/CD.yml index a5bb0fb..59835f7 100644 --- a/.github/workflows/CD.yml +++ b/.github/workflows/CD.yml @@ -16,7 +16,7 @@ jobs: - name: Install .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.0.100 + dotnet-version: 3.1.100 - name: Pack run: dotnet pack CliFx --configuration Release diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 10377af..4c485db 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -13,7 +13,7 @@ jobs: - name: Install .NET Core uses: actions/setup-dotnet@v1 with: - dotnet-version: 3.0.100 + dotnet-version: 3.1.100 - name: Build & test run: dotnet test --configuration Release diff --git a/CliFx.Benchmarks/Benchmark.cs b/CliFx.Benchmarks/Benchmark.cs index c3f133d..ac88b01 100644 --- a/CliFx.Benchmarks/Benchmark.cs +++ b/CliFx.Benchmarks/Benchmark.cs @@ -4,17 +4,17 @@ using CliFx.Benchmarks.Commands; namespace CliFx.Benchmarks { - [CoreJob] + [SimpleJob] [RankColumn] public class Benchmark { private static readonly string[] Arguments = {"--str", "hello world", "-i", "13", "-b"}; [Benchmark(Description = "CliFx", Baseline = true)] - public Task ExecuteWithCliFx() => new CliApplicationBuilder().AddCommand(typeof(CliFxCommand)).Build().RunAsync(Arguments); + public async ValueTask ExecuteWithCliFx() => await new CliApplicationBuilder().AddCommand(typeof(CliFxCommand)).Build().RunAsync(Arguments); [Benchmark(Description = "System.CommandLine")] - public Task ExecuteWithSystemCommandLine() => new SystemCommandLineCommand().ExecuteAsync(Arguments); + public async ValueTask ExecuteWithSystemCommandLine() => await new SystemCommandLineCommand().ExecuteAsync(Arguments); [Benchmark(Description = "McMaster.Extensions.CommandLineUtils")] public int ExecuteWithMcMaster() => McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute(Arguments); diff --git a/CliFx.Benchmarks/CliFx.Benchmarks.csproj b/CliFx.Benchmarks/CliFx.Benchmarks.csproj index 1923fa8..a3d0602 100644 --- a/CliFx.Benchmarks/CliFx.Benchmarks.csproj +++ b/CliFx.Benchmarks/CliFx.Benchmarks.csproj @@ -3,14 +3,14 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - + - + diff --git a/CliFx.Benchmarks/Commands/CliFxCommand.cs b/CliFx.Benchmarks/Commands/CliFxCommand.cs index 2c6481e..a2f1d3a 100644 --- a/CliFx.Benchmarks/Commands/CliFxCommand.cs +++ b/CliFx.Benchmarks/Commands/CliFxCommand.cs @@ -16,6 +16,6 @@ namespace CliFx.Benchmarks.Commands [CommandOption("bool", 'b')] public bool BoolOption { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Demo/CliFx.Demo.csproj b/CliFx.Demo/CliFx.Demo.csproj index 2b44b0e..068f599 100644 --- a/CliFx.Demo/CliFx.Demo.csproj +++ b/CliFx.Demo/CliFx.Demo.csproj @@ -3,12 +3,12 @@ Exe - netcoreapp3.0 + netcoreapp3.1 - - + + diff --git a/CliFx.Demo/Commands/BookAddCommand.cs b/CliFx.Demo/Commands/BookAddCommand.cs index c94e355..5c0c14f 100644 --- a/CliFx.Demo/Commands/BookAddCommand.cs +++ b/CliFx.Demo/Commands/BookAddCommand.cs @@ -31,7 +31,7 @@ namespace CliFx.Demo.Commands _libraryService = libraryService; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { // To make the demo simpler, we will just generate random publish date and ISBN if they were not set if (Published == default) @@ -48,7 +48,7 @@ namespace CliFx.Demo.Commands console.Output.WriteLine("Book added."); console.RenderBook(book); - return Task.CompletedTask; + return default; } } diff --git a/CliFx.Demo/Commands/BookCommand.cs b/CliFx.Demo/Commands/BookCommand.cs index 2a4fb81..8fdb252 100644 --- a/CliFx.Demo/Commands/BookCommand.cs +++ b/CliFx.Demo/Commands/BookCommand.cs @@ -20,7 +20,7 @@ namespace CliFx.Demo.Commands _libraryService = libraryService; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { var book = _libraryService.GetBook(Title); @@ -29,7 +29,7 @@ namespace CliFx.Demo.Commands console.RenderBook(book); - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Demo/Commands/BookListCommand.cs b/CliFx.Demo/Commands/BookListCommand.cs index e6077fe..8740727 100644 --- a/CliFx.Demo/Commands/BookListCommand.cs +++ b/CliFx.Demo/Commands/BookListCommand.cs @@ -16,7 +16,7 @@ namespace CliFx.Demo.Commands _libraryService = libraryService; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { var library = _libraryService.GetLibrary(); @@ -32,7 +32,7 @@ namespace CliFx.Demo.Commands console.RenderBook(book); } - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Demo/Commands/BookRemoveCommand.cs b/CliFx.Demo/Commands/BookRemoveCommand.cs index ce7e771..f5239c9 100644 --- a/CliFx.Demo/Commands/BookRemoveCommand.cs +++ b/CliFx.Demo/Commands/BookRemoveCommand.cs @@ -19,7 +19,7 @@ namespace CliFx.Demo.Commands _libraryService = libraryService; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { var book = _libraryService.GetBook(Title); @@ -30,7 +30,7 @@ namespace CliFx.Demo.Commands console.Output.WriteLine($"Book {Title} removed."); - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Demo/Program.cs b/CliFx.Demo/Program.cs index b699782..72850d5 100644 --- a/CliFx.Demo/Program.cs +++ b/CliFx.Demo/Program.cs @@ -25,11 +25,11 @@ namespace CliFx.Demo return services.BuildServiceProvider(); } - public static Task Main(string[] args) + public static async Task Main(string[] args) { var serviceProvider = ConfigureServices(); - return new CliApplicationBuilder() + return await new CliApplicationBuilder() .AddCommandsFromThisAssembly() .UseCommandFactory(schema => (ICommand) serviceProvider.GetRequiredService(schema.Type)) .Build() diff --git a/CliFx.Tests/CliFx.Tests.csproj b/CliFx.Tests/CliFx.Tests.csproj index cfa30a7..46a7546 100644 --- a/CliFx.Tests/CliFx.Tests.csproj +++ b/CliFx.Tests/CliFx.Tests.csproj @@ -2,7 +2,7 @@ - netcoreapp3.0 + netcoreapp3.1 false true true diff --git a/CliFx.Tests/TestCommands/CancellableCommand.cs b/CliFx.Tests/TestCommands/CancellableCommand.cs index 5b6cbfd..5546fda 100644 --- a/CliFx.Tests/TestCommands/CancellableCommand.cs +++ b/CliFx.Tests/TestCommands/CancellableCommand.cs @@ -8,7 +8,7 @@ namespace CliFx.Tests.TestCommands [Command("cancel")] public class CancellableCommand : ICommand { - public async Task ExecuteAsync(IConsole console) + public async ValueTask ExecuteAsync(IConsole console) { await Task.Yield(); diff --git a/CliFx.Tests/TestCommands/CommandExceptionCommand.cs b/CliFx.Tests/TestCommands/CommandExceptionCommand.cs index ffefaab..303dd1e 100644 --- a/CliFx.Tests/TestCommands/CommandExceptionCommand.cs +++ b/CliFx.Tests/TestCommands/CommandExceptionCommand.cs @@ -14,6 +14,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("msg", 'm')] public string? Message { get; set; } - public Task ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode); + public ValueTask ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode); } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/ConcatCommand.cs b/CliFx.Tests/TestCommands/ConcatCommand.cs index e357230..a864e67 100644 --- a/CliFx.Tests/TestCommands/ConcatCommand.cs +++ b/CliFx.Tests/TestCommands/ConcatCommand.cs @@ -14,10 +14,10 @@ namespace CliFx.Tests.TestCommands [CommandOption('s', Description = "String separator.")] public string Separator { get; set; } = ""; - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { console.Output.WriteLine(string.Join(Separator, Inputs)); - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/DivideCommand.cs b/CliFx.Tests/TestCommands/DivideCommand.cs index 71dad4e..8e95992 100644 --- a/CliFx.Tests/TestCommands/DivideCommand.cs +++ b/CliFx.Tests/TestCommands/DivideCommand.cs @@ -16,10 +16,10 @@ namespace CliFx.Tests.TestCommands // This property should be ignored by resolver public bool NotAnOption { get; set; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { console.Output.WriteLine(Dividend / Divisor); - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/DuplicateOptionNamesCommand.cs b/CliFx.Tests/TestCommands/DuplicateOptionNamesCommand.cs index 61f313c..064e827 100644 --- a/CliFx.Tests/TestCommands/DuplicateOptionNamesCommand.cs +++ b/CliFx.Tests/TestCommands/DuplicateOptionNamesCommand.cs @@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("fruits")] public string? Oranges { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/DuplicateOptionShortNamesCommand.cs b/CliFx.Tests/TestCommands/DuplicateOptionShortNamesCommand.cs index 542d69f..39c87b7 100644 --- a/CliFx.Tests/TestCommands/DuplicateOptionShortNamesCommand.cs +++ b/CliFx.Tests/TestCommands/DuplicateOptionShortNamesCommand.cs @@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands [CommandOption('f')] public string? Oranges { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/EnvironmentVariableCommand.cs b/CliFx.Tests/TestCommands/EnvironmentVariableCommand.cs index 46eacd4..1ccdd81 100644 --- a/CliFx.Tests/TestCommands/EnvironmentVariableCommand.cs +++ b/CliFx.Tests/TestCommands/EnvironmentVariableCommand.cs @@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("opt", EnvironmentVariableName = "ENV_SINGLE_VALUE")] public string? Option { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } diff --git a/CliFx.Tests/TestCommands/EnvironmentVariableWithMultipleValuesCommand.cs b/CliFx.Tests/TestCommands/EnvironmentVariableWithMultipleValuesCommand.cs index 3eaf40d..b33e721 100644 --- a/CliFx.Tests/TestCommands/EnvironmentVariableWithMultipleValuesCommand.cs +++ b/CliFx.Tests/TestCommands/EnvironmentVariableWithMultipleValuesCommand.cs @@ -11,6 +11,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("opt", EnvironmentVariableName = "ENV_MULTIPLE_VALUES")] public IEnumerable? Option { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } diff --git a/CliFx.Tests/TestCommands/EnvironmentVariableWithoutCollectionPropertyCommand.cs b/CliFx.Tests/TestCommands/EnvironmentVariableWithoutCollectionPropertyCommand.cs index 872ac91..68b747c 100644 --- a/CliFx.Tests/TestCommands/EnvironmentVariableWithoutCollectionPropertyCommand.cs +++ b/CliFx.Tests/TestCommands/EnvironmentVariableWithoutCollectionPropertyCommand.cs @@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("opt", EnvironmentVariableName = "ENV_MULTIPLE_VALUES")] public string? Option { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } diff --git a/CliFx.Tests/TestCommands/ExceptionCommand.cs b/CliFx.Tests/TestCommands/ExceptionCommand.cs index 9c7f478..d78b350 100644 --- a/CliFx.Tests/TestCommands/ExceptionCommand.cs +++ b/CliFx.Tests/TestCommands/ExceptionCommand.cs @@ -11,6 +11,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("msg", 'm')] public string? Message { get; set; } - public Task ExecuteAsync(IConsole console) => throw new Exception(Message); + public ValueTask ExecuteAsync(IConsole console) => throw new Exception(Message); } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/HelloWorldDefaultCommand.cs b/CliFx.Tests/TestCommands/HelloWorldDefaultCommand.cs index fd8eb9b..7490130 100644 --- a/CliFx.Tests/TestCommands/HelloWorldDefaultCommand.cs +++ b/CliFx.Tests/TestCommands/HelloWorldDefaultCommand.cs @@ -7,10 +7,10 @@ namespace CliFx.Tests.TestCommands [Command] public class HelloWorldDefaultCommand : ICommand { - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { console.Output.WriteLine("Hello world."); - return Task.CompletedTask; + return default; } } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/HelpDefaultCommand.cs b/CliFx.Tests/TestCommands/HelpDefaultCommand.cs index c130217..f388826 100644 --- a/CliFx.Tests/TestCommands/HelpDefaultCommand.cs +++ b/CliFx.Tests/TestCommands/HelpDefaultCommand.cs @@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("option-b", 'b', Description = "OptionB description.")] public string? OptionB { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/HelpNamedCommand.cs b/CliFx.Tests/TestCommands/HelpNamedCommand.cs index c6e3891..4fda3e5 100644 --- a/CliFx.Tests/TestCommands/HelpNamedCommand.cs +++ b/CliFx.Tests/TestCommands/HelpNamedCommand.cs @@ -13,6 +13,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("option-d", 'd', Description = "OptionD description.")] public string? OptionD { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/HelpSubCommand.cs b/CliFx.Tests/TestCommands/HelpSubCommand.cs index e284532..152498a 100644 --- a/CliFx.Tests/TestCommands/HelpSubCommand.cs +++ b/CliFx.Tests/TestCommands/HelpSubCommand.cs @@ -10,6 +10,6 @@ namespace CliFx.Tests.TestCommands [CommandOption("option-e", 'e', Description = "OptionE description.")] public string? OptionE { get; set; } - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx.Tests/TestCommands/NonAnnotatedCommand.cs b/CliFx.Tests/TestCommands/NonAnnotatedCommand.cs index 6977532..65aeedf 100644 --- a/CliFx.Tests/TestCommands/NonAnnotatedCommand.cs +++ b/CliFx.Tests/TestCommands/NonAnnotatedCommand.cs @@ -5,6 +5,6 @@ namespace CliFx.Tests.TestCommands { public class NonAnnotatedCommand : ICommand { - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + public ValueTask ExecuteAsync(IConsole console) => default; } } \ No newline at end of file diff --git a/CliFx/CliApplication.cs b/CliFx/CliApplication.cs index 8b21f02..7159e9f 100644 --- a/CliFx/CliApplication.cs +++ b/CliFx/CliApplication.cs @@ -43,7 +43,7 @@ namespace CliFx _helpTextRenderer = helpTextRenderer; } - private async Task HandleDebugDirectiveAsync(CommandInput commandInput) + private async ValueTask HandleDebugDirectiveAsync(CommandInput commandInput) { // Debug mode is enabled if it's allowed in the application and it was requested via corresponding directive var isDebugMode = _configuration.IsDebugModeAllowed && commandInput.IsDebugDirectiveSpecified(); @@ -162,7 +162,7 @@ namespace CliFx return isError ? -1 : 0; } - private async Task HandleCommandExecutionAsync(CommandInput commandInput, CommandSchema targetCommandSchema) + private async ValueTask HandleCommandExecutionAsync(CommandInput commandInput, CommandSchema targetCommandSchema) { // Create an instance of the command var command = _commandFactory.CreateCommand(targetCommandSchema); @@ -178,7 +178,7 @@ namespace CliFx } /// - public async Task RunAsync(IReadOnlyList commandLineArguments) + public async ValueTask RunAsync(IReadOnlyList commandLineArguments) { try { diff --git a/CliFx/CliFx.csproj b/CliFx/CliFx.csproj index fb0c0ab..f42e701 100644 --- a/CliFx/CliFx.csproj +++ b/CliFx/CliFx.csproj @@ -2,7 +2,7 @@ - net45;netstandard2.0;netstandard2.1 + netstandard2.1;netstandard2.0;net45 $(Company) Declarative framework for CLI applications command line executable interface framework parser arguments net core @@ -18,15 +18,18 @@ snupkg - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/CliFx/ICliApplication.cs b/CliFx/ICliApplication.cs index 573d470..019c21b 100644 --- a/CliFx/ICliApplication.cs +++ b/CliFx/ICliApplication.cs @@ -11,6 +11,6 @@ namespace CliFx /// /// Runs application with specified command line arguments and returns an exit code. /// - Task RunAsync(IReadOnlyList commandLineArguments); + ValueTask RunAsync(IReadOnlyList commandLineArguments); } } \ No newline at end of file diff --git a/CliFx/ICommand.cs b/CliFx/ICommand.cs index 9fb2c78..9c5826f 100644 --- a/CliFx/ICommand.cs +++ b/CliFx/ICommand.cs @@ -12,6 +12,6 @@ namespace CliFx /// Executes command using specified implementation of . /// This method is called when the command is invoked by a user through command line interface. /// - Task ExecuteAsync(IConsole console); + ValueTask ExecuteAsync(IConsole console); } } \ No newline at end of file diff --git a/Readme.md b/Readme.md index f23d2e9..350c8a2 100644 --- a/Readme.md +++ b/Readme.md @@ -59,8 +59,8 @@ The following code will create and run default `CliApplication` that will resolv ```c# public static class Program { - public static Task Main(string[] args) => - new CliApplicationBuilder() + public static async Task Main(string[] args) => + await new CliApplicationBuilder() .AddCommandsFromThisAssembly() .Build() .RunAsync(args); @@ -85,17 +85,17 @@ public class LogCommand : ICommand [CommandOption("base", 'b', Description = "Logarithm base.")] public double Base { get; set; } = 10; - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { var result = Math.Log(Value, Base); console.Output.WriteLine(result); - return Task.CompletedTask; + return default; } } ``` -By implementing `ICommand` this class also provides `ExecuteAsync` method. This is the method that gets called when the user invokes the command. Its return type is `Task` in order to facilitate asynchronous execution, but if your command runs synchronously you can simply return `Task.CompletedTask`. +By implementing `ICommand` this class also provides `ExecuteAsync` method. This is the method that gets called when the user invokes the command. Its return type is `ValueTask` in order to facilitate both synchronous and asynchronous execution. If your command always runs synchronously, simply return `default` at the end of the method. The `ExecuteAsync` method also takes an instance of `IConsole` as a parameter. You should use the `console` parameter in places where you would normally use `System.Console`, in order to make your command testable. @@ -171,7 +171,7 @@ public class DivideCommand : ICommand [CommandOption("divisor", IsRequired = true)] public double Divisor { get; set; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { if (Math.Abs(Divisor) < double.Epsilon) { @@ -182,7 +182,7 @@ public class DivideCommand : ICommand var result = Dividend / Divisor; console.Output.WriteLine(result); - return Task.CompletedTask; + return default; } } ``` @@ -225,7 +225,7 @@ Cancelled or terminated app returns non-zero exit code. [Command("cancel")] public class CancellableCommand : ICommand { - public async Task ExecuteAsync(IConsole console) + public async ValueTask ExecuteAsync(IConsole console) { console.Output.WriteLine("Printed"); @@ -248,7 +248,7 @@ For example, here is how you would configure your application to use [`Microsoft ```c# public static class Program { - public static Task Main(string[] args) + public static async Task Main(string[] args) { var services = new ServiceCollection(); @@ -260,7 +260,7 @@ public static class Program var serviceProvider = services.BuildServiceProvider(); - return new CliApplicationBuilder() + return await new CliApplicationBuilder() .AddCommandsFromThisAssembly() .UseCommandFactory(schema => (ICommand) serviceProvider.GetRequiredService(schema.Type)) .Build() @@ -285,7 +285,7 @@ public class UserAddCommand : ICommand [CommandOption("email", 'e')] public string Email { get; set; } - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { var validationResult = new UserAddCommandValidator().Validate(this); if (!validationResult.IsValid) @@ -358,13 +358,13 @@ public class ConcatCommand : ICommand [CommandOption("right")] public string Right { get; set; } = "world"; - public Task ExecuteAsync(IConsole console) + public ValueTask ExecuteAsync(IConsole console) { console.Output.Write(Left); console.Output.Write(' '); console.Output.Write(Right); - return Task.CompletedTask; + return default; } } ```