Command Line Subcommands in C#

Here’s the translation of the Go code to C# with explanations in Markdown format suitable for Hugo:

Our program demonstrates how to implement command-line subcommands with their own set of flags. This is similar to how tools like git have subcommands (e.g., git commit, git push) with their own specific flags.

using System;
using System.Collections.Generic;
using CommandLine;
using CommandLine.Text;

class Program
{
    [Verb("foo", HelpText = "Perform foo operation.")]
    class FooOptions
    {
        [Option('e', "enable", Default = false, HelpText = "Enable the foo operation.")]
        public bool Enable { get; set; }

        [Option('n', "name", Default = "", HelpText = "Name for foo operation.")]
        public string Name { get; set; }

        [Value(0)]
        public IEnumerable<string> AdditionalArguments { get; set; }
    }

    [Verb("bar", HelpText = "Perform bar operation.")]
    class BarOptions
    {
        [Option('l', "level", Default = 0, HelpText = "Level for bar operation.")]
        public int Level { get; set; }

        [Value(0)]
        public IEnumerable<string> AdditionalArguments { get; set; }
    }

    static int Main(string[] args)
    {
        return CommandLine.Parser.Default.ParseArguments<FooOptions, BarOptions>(args)
            .MapResult(
                (FooOptions opts) => RunFoo(opts),
                (BarOptions opts) => RunBar(opts),
                errs => 1);
    }

    static int RunFoo(FooOptions opts)
    {
        Console.WriteLine("subcommand 'foo'");
        Console.WriteLine($"  enable: {opts.Enable}");
        Console.WriteLine($"  name: {opts.Name}");
        Console.WriteLine($"  tail: [{string.Join(", ", opts.AdditionalArguments)}]");
        return 0;
    }

    static int RunBar(BarOptions opts)
    {
        Console.WriteLine("subcommand 'bar'");
        Console.WriteLine($"  level: {opts.Level}");
        Console.WriteLine($"  tail: [{string.Join(", ", opts.AdditionalArguments)}]");
        return 0;
    }
}

In this C# version, we’re using the popular CommandLineParser library to handle command-line parsing. This library provides a more declarative way to define command-line options compared to manually parsing arguments.

We define two classes, FooOptions and BarOptions, which represent the options for our two subcommands. These classes use attributes to define the flags and their properties.

The Main method uses the CommandLineParser to parse the arguments and map them to the appropriate subcommand handler.

To run the program:

$ dotnet run -- foo -e -n=joe a1 a2
subcommand 'foo'
  enable: True
  name: joe
  tail: [a1, a2]

$ dotnet run -- bar -l 8 a1
subcommand 'bar'
  level: 8
  tail: [a1]

If you try to use a flag that doesn’t belong to a subcommand, you’ll get an error:

$ dotnet run -- bar -e a1
bar: Unknown option: 'e'

This approach provides a structured way to handle subcommands in C#, with each subcommand having its own set of flags and arguments.

Next, we’ll look at environment variables, another common way to parameterize programs.