Building a Powerful Console App in C# with .NET and System.CommandLine
Basic Handling of Command-Line Arguments in .NET Console Apps
Creating console applications often starts with handling command-line arguments. In .NET, this is done by parsing the args
array provided to the Main
method. This basic approach allows developers to capture user input and change the application’s behavior based on it. While this method works well for simple applications, it can become more difficult to manage as the number of parameters and complexity grow. In this section, we’ll look at the traditional way of handling command-line arguments, highlighting its simplicity and the challenges it can present as applications become more complex.
Before we proceed with our demonstration of the .NET Console application, it’s essential to clarify that this example is primarily designed to showcase the base functionalities. It may not be optimized or written in the most efficient manner.
Let’s consider a simple console application that takes a name and age as input and then outputs a message to the user. We’ll use the traditional approach of parsing the args
array.
using System; class Program { static void Main(string[] args) { if (args.Length != 4) { Console.WriteLine("Usage: ConsoleApp --name <name> --age <age>"); return; } string name = string.Empty; int age = 0; if (args[0] == "--name" && args[2] == "--age") { name = args[1]; if (!int.TryParse(args[3], out age)) { Console.WriteLine("Invalid age. Please provide a valid number."); return; } } else if (args[0] == "--age" && args[2] == "--name") { if (!int.TryParse(args[1], out age)) { Console.WriteLine("Invalid age. Please provide a valid number."); return; } name = args[3]; } else { Console.WriteLine("Usage: ConsoleApp --name <name> --age <age>"); return; } if (name != null && age > 0) { Console.WriteLine($"Hello, {name}! You are {age} years old."); } } }
What can I say? the code sucks and would not pass any code review but we only need examples. Eventually we can optimise it by maybe creating classes like Parser and Commander that check and execute the passed parameters correctly, but this takes time.
Explanation:
- Argument Count Check: The application first checks if exactly 4 arguments are provided. If not, it shows the usage instructions and exits.
- First Argument Check: The application then checks if the first argument is
--name
and the third argument is--age
. - Second Argument Check: If the first check is true, it assigns the values of
name
andage
from the appropriate positions. If the age is not a valid integer, it shows an error message and exits. - Alternative Argument Order: If the first argument is
--age
and the third argument is--name
, it handles the arguments in this order as well. - Invalid Argument Order: If neither condition is met, it shows the usage instructions.
- Output: If all conditions are met and the arguments are valid, it outputs the personalized greeting message.
Let’s try running our app:
dotnet run --name Otto --age 40 dotnet run --age 30 --name Marc Hello, John! You are 30 years old.
This example demonstrates the traditional approach of handling command-line arguments in a .NET console application. As you can see, even with a small number of parameters, the code can quickly become cluttered and error-prone, highlighting the need for a more structured approach as complexity grows.
Simplify Command-Line App Development with System.CommandLine
System.CommandLine, maintained by Microsoft, is a powerful library designed to streamline the development of command-line applications. In its current preview beta, it offers essential functionality for parsing command-line input and generating help text, freeing developers from writing boilerplate code.
This section introduces System.CommandLine, emphasizing its role in simplifying app development by allowing developers to focus on writing application logic rather than parsing command-line arguments. Despite its prerelease status, the library is widely used, including by Microsoft’s .NET CLI and various other tools. With System.CommandLine, developers can create fast, lightweight, and AOT-capable CLI applications with ease, while benefitting from ongoing support and updates from Microsoft.
Run the following command:
dotnet add package System.CommandLine --prerelease
dotnet add package System.CommandLine.NamingConventionBinder --prerelease
Let’s get started and see how easy it is to convert our complex app with a few lines of code:
using System.CommandLine.Invocation; using System.CommandLine.NamingConventionBinder; class Program { static int Main(string[] args) { var rootCommand = new RootCommand { new Option<string>("--name", "The name of the person to greet"), new Option<int>("--age", "The age of the person to greet") }; rootCommand.Description = "Greets a person by name and age."; rootCommand.Handler = CommandHandler.Create<string, int>((name, age) => { Console.WriteLine($"Hello, {name}! You are {age} years old."); }); return rootCommand.Invoke(args); } }
Explanation:
- Command Definition: We define a
RootCommand
object, which represents the main command of our application. - Options: We add two options to the
RootCommand
:--name
and--age
, representing the name and age of the person to greet, respectively. - Description: We set a description for the
RootCommand
to provide context for the application’s purpose. - Handler: We define a command handler using
CommandHandler.Create
, specifying the types of arguments (string
andint
) that the handler expects. Inside the handler, we simply print a greeting message using the provided name and age. - Invocation: We invoke the
RootCommand
with the provided command-line arguments usingrootCommand.Invoke(args)
.
This approach significantly simplifies the code compared to manually parsing the args
array. System.CommandLine takes care of parsing the command-line arguments and passing them to the handler, allowing us to focus on the application logic.
Simplifying User Interaction with Automated Help Generation
System.CommandLine’s automatic help generation is a significant advantage. In addition to simplifying command-line interface development, it offers the built-in functionality to generate comprehensive help messages. This feature not only saves time and effort in documenting commands and options but also ensures that users can quickly understand how to use your program without the need for external documentation.
This is an example of our program without entering the parameters:
Greets a person by name and age. Usage: ConsoleApp [options] Options: --name <name> The name of the person to greet --age <age> The age of the person to greet --version Show version information -?, -h, --help Show help and usage information Use "ConsoleApp [command] --help" for more information about a command.
Empowering Console Applications: Concluding Remarks
As I was looking for ways to improve my console applications, I came across the System.CommandLine library and was impressed by how easy it was to use. Handling parameters in console apps can be tricky, but System.CommandLine makes it simple with lots of useful features. Even though it’s still in beta, I didn’t hesitate to use it in my own projects and highly recommend it. Plus, knowing that it’s supported by Microsoft gives me confidence in its reliability. Are you thinking about giving it a try? I’d love to hear your thoughts!
If you think your friends or network would find this article useful, please consider sharing it with them. Your support is greatly appreciated.
Thanks for reading!
Robert
Thanks for the article. I’m looking at System.CommandLine but I see it has never been released (it is at 2.0.0 beta, there was no 1.0.0 release after more than 5 years). The Microsoft documentation says it is “prerelease” and there may be significant changes prior to release.
Are you concerned that the System.CommandLine library has not been released?
Ottorino Bruni
Hi Robert, thanks for reading the article and your comment. yes, I wrote that it is in beta and what I think in the conclusion of the article. thanks