How do you tell if an option was specified when using System.CommandLine?

Viewed 898

With a root command:

new RootCommand
{
    new Option<string>("--myoption")
};

how do you tell the difference between

./myapp

and

./myapp --myoption ""

?

I initially assumed that the option would be null if it wasn't specified, but it's not, it's an empty string :( Adding an explicit default of null also doesn't work; this code still prints out "" when no options are passed in:

static void Main(string[] args)
{
    var rootCommand = new RootCommand
    {
        new Option<string>("--myoption", () => null)
    };
    rootCommand.Handler = CommandHandler.Create<string>(Run);
    rootCommand.Invoke(args);
}

private static void Run(string myoption)
{
    Console.WriteLine(myoption == null ? "(null)" : '"' + myoption + '"');
}

If the default is set to a non-null string, the default does come through as expected; only null is mysteriously changed into an empty string.

1 Answers

You can describe a function to calculate a default value. If using C# 8 or newer, you may need to explicitly say that your string is nullable by adding the question mark at the end.

new RootCommand
{
    new Option<string?>("--myoption", () => null, "My option. Defaults to null");
};

I would have thought that this would work, but I was able to set up a working example on dotnetfiddle here https://dotnetfiddle.net/uxyC8Y which shows even with every parameter marked as nullable, it is still being returned as an empty string. This may be an issue with the System.CommandLine project, so I put in an issue here https://github.com/dotnet/command-line-api/issues/1459

EDIT: This issue was resolved just 2 days ago with this commit https://github.com/dotnet/command-line-api/pull/1458/files it will take some time before this fix shows up in a published NuGet package, but it will be fixed eventually in future versions of the library.

Without being able to use nulls, my only suggestion would be to use a very distinct default string to mark a value as not assigned.

const string defaultString = "Not Assigned.";
static void Main(string[] args)
{
    var rootCommand = new RootCommand
    {
        new Option<string>("--myoption", () => defaultString)
    };
    rootCommand.Handler = CommandHandler.Create<string>(Run);
    rootCommand.Invoke(args);
}

private static void Run(string myoption)
{
    Console.WriteLine(myoption == defaultString ? "(null)" : '"' + myoption + '"');
}
Related