Page 28 - MSDN Magazine, March 2019
P. 28
(specified in the summary XML comment),
but also for the documentation on each
argument using parameter XML comments.
Leveraging the XML comments requires
enabling doc output, but this is configured
automatically for you when referencing
the assembly that enables configuration
via Main. There’s built-in help output with
any of three command-line options: -h, -?,
or --help. For example, the help displayed
in Figure 1 is automatically generated by System.CommandLine.
Similarly, while there’s no version parameter on Main, System.Com- mandLine automatically generates a --version option that outputs the assembly version of the executable.
Another feature, command-line syntax verification, detects if a
required argument (for which no default is specified on the parameter)
is missing. If a required argument isn’t specified, System.Command-
Line automatically issues an error that reads, “Required argument
missing for option: --output.” Although somewhat counterintuitive,
by default options with arguments are required. However, if the
argument value associated with an option isn’t required, you can
leverage C# default parameter value syntax. For example:
int xCropSize = 0
There’s also built-in support for parsing options regardless of the sequence in which the options appear on the command line. And it’s worth noting that the delimiter between the option and the argument may be a space, a colon or an equal sign by default. Finally, Camel casing on Main’s parameter names is converted to Posix-style argument names (that is, xCropSize translates to --x-crop-size on the command line).
If you type an unrecognized option or command name, System.CommandLine automatically returns a command-line error that reads, “Unrecognized command or argument ....” How- ever, if the name specified is similar to an existing option, the error will prompt with a typo correction suggestion.
There are some built-in directives available to all command-line
applications that use System.CommandLine. These directives are
enclosed in square brackets and appear immediately following
the application name. For example, the [debug] directive triggers
a breakpoint that allows you to attach a debugger, while [parse]
displays a preview of how tokens are parsed, as shown here:
imageconv [parse] --input sunrise.CR2 --output sunrise.JPG
In addition, automated testing via an IConsole interface and TestConsole class implementation is supported. To inject the TestConsole into the command-line pipeline, add an IConsole
parameter to Main, like so:
public static void Main(
FileInfo input, FileInfo output,
int xCropSize = 0, int yCropSize = 0,
IConsole console = null)
To leverage the console parameter, replace invocations to Sys- tem.Console with the IConsole parameter. Note that the IConsole parameter will be set automatically for you when invoked directly from the command line (rather than from a unit test), so even though the parameter is assigned null by default, it shouldn’t have a null value unless you write test code that invokes it that way. Alternatively, consider putting the IConsole parameter first.
imageconv
app ...
... ... ... ...
App Models not created yet.
Your App
One of my favorite features is support for tab completion, which end users can opt into by running a command to activate it (see bit.ly/2sSRsQq). This is an opt-in scenario because users tend to be protective of implicit changes to the shell. Tab completion for options and command names happens automatically, but there’s also tab completion for arguments via suggestions. When config- uring a command or option, the tab completion values can come from a static list of values, such as the q, m, n, d or diagnostic values of --verbosity. Or they can be dynamically provided at run time, such as from REST invocation that returns a list of available NuGet packages when the argument is a NuGet reference.
The mapping between command-line arguments and Main method parameters is basic today, but still relatively capable for lots of programs.
Using the Main method as the specification for the com- mand line is just one of several ways that you can program using System.CommandLine. The architecture is flexible, allowing other ways to define and work with the command line.
The System.CommandLine Architecture
System.CommandLine is architected around a core assembly that includes an API for configuring the command line and a parser that resolves the command-line arguments into a data structure. All the features listed in the previous section can be enabled via the core assembly, except for enabling a different method signature for Main. However, support for configuring the command line, specifically using a domain-specific language (such as a Main like method) is enabled by an app model. (The app model used for the Main like method implementation described earlier is code-named “DragonFruit.”) However, the System.CommandLine architecture enables support for additional app models (as shown in Figure 3).
For example, you could write an app model that uses a C# class model to define the command-line syntax for an application. In such a model, property names might correspond to the option name and the property type would correspond to the data type into which to
Configure from Main
Core (System.CommandLine)
Class Model
docopt
...
Figure 3 System.CommandLine Architecture
22 msdn magazine
Essential .NET