8

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.

2 Answers 2

3

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 + '"');
}
Sign up to request clarification or add additional context in comments.

2 Comments

I tried that; unfortunately with an explicit default of null, it still actually default to an empty string! If I have an explicit default of anything apart from null, then that works, but null does not. I will update the question.
@MarkRaymond are you using C# 8 with nullable references endjin.com/blog/2020/10/… ? I think by default C# 8 will not allow null values, unless you explicitly say that nulls are allowed. I'll update my answer with what should fix it.
0

EDIT 7th August 2025:

As of System.CommandLine version 2.0.0-beta6.25358.103, there is now an OptionResult.Implicit property which does the same thing. This will work great if you're on the new version, but unfortunately the entire interface has changed compared to the old version, so if you're still stuck on an older version that doesn't have this feature, then my answer below will help you out.

EDIT END

You can modify the default value factory so that it changes a value if the function is invoked, and then check if that value has been changed.

private static void Main(string[] args)
{
    RootCommand rootCommand = [_myOption];
    rootCommand.SetHandler(Run, _myOption);
    rootCommand.Invoke(args);
}

private static readonly Option<string> _myOption = new("--my-option", GetDefaultValue_MyOption);
private static bool _myOptionExplicit = true;
private static string GetDefaultValue_MyOption()
{
    _myOptionExplicit = false;
    return "hello";
}

private static void Run(string myOption)
{
    string ex_implicitly = _myOptionExplicit ? "explicitly" : "implicitly";
    Console.WriteLine($"option {myOption} was specified {ex_implicitly}");
}

// this produces the following outputs:
//
// .\MyApp
//     option hello was specified implicitly
//
// .\MyApp --my-option hello
//     option hello was specified explicitly
//
// .\MyApp --my-option goodbye
//     option goodbye was specified explicitly

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.