I would like to pass several numbers to a C# console app and have prepared a .Net Fiddle to demonstrate my issue:
private const long DEFAULT_ALPHA_VALUE = 1234567890L;
private static long[] alphas = { DEFAULT_ALPHA_VALUE };
public static async Task Main()
{
Option<long[]> alphaOption = new
(
aliases: new[] { "-a", "--alpha" },
getDefaultValue: () => new[] { DEFAULT_ALPHA_VALUE },
description: "Numerical alpha values"
);
RootCommand rootCommand = new("A test app for multiple numerical values option");
rootCommand.AddGlobalOption(alphaOption);
rootCommand.SetHandler(a => { alphas = a; }, alphaOption);
await RunInvokeAsync(rootCommand, "-a", "1234");
await RunInvokeAsync(rootCommand, "-a", "1234", "5678"); // Unrecognized command or argument '5678'.
await RunInvokeAsync(rootCommand, "-a", "1234 5678"); // Cannot parse argument '1234 5678' for option '-a' as expected type 'System.Int64'.
await RunInvokeAsync(rootCommand, "-a", "1234,5678"); // Cannot parse argument '1234 5678' for option '-a' as expected type 'System.Int64'.
}
private static async Task RunInvokeAsync(RootCommand rootCommand, params string[] args)
{
int status = await rootCommand.InvokeAsync(args);
Console.WriteLine($"args: {JsonSerializer.Serialize(args)}, status: {status}, alphas: {JsonSerializer.Serialize(alphas)}");
}
I was expecting being able to pass several numbers by running one of the following commands at the CLI:
dotnet run --project MultipleValuesOption.csproj -- -a 1234 5678
dotnet run --project MultipleValuesOption.csproj -- -a 1234,5678
But as you can see in the screenshot there are runtime errors:
My question is: how do you pass several numbers, am I picking them wrongly in my C# code or do I maybe call the C# app in a wrong way?
Update:
I have followed the great suggestion by Serg and have added the
{ AllowMultipleArgumentsPerToken = true }
, but unfortunately there is still a problem, when you add a second option as shown in my new .Net Fiddle:
private const long DEFAULT_ALPHA_VALUE = 13579L;
private static long[] alphas = { DEFAULT_ALPHA_VALUE };
private const long DEFAULT_BETA_VALUE = 24680L;
private static long[] betas = { DEFAULT_BETA_VALUE };
public static async Task Main(string[] args)
{
Option<long[]> alphaOption = new
(
aliases: new[] { "-a", "--alpha" },
getDefaultValue: () => new[] { DEFAULT_ALPHA_VALUE },
description: "Numerical alpha values"
)
{
AllowMultipleArgumentsPerToken = true,
};
Option<long[]> betaOption = new
(
aliases: new[] { "-b", "--beta" },
getDefaultValue: () => new[] { DEFAULT_BETA_VALUE },
description: "Numerical beta values"
)
{
AllowMultipleArgumentsPerToken = true,
};
RootCommand rootCommand = new("A test app for multiple numerical values option");
rootCommand.AddGlobalOption(alphaOption);
rootCommand.AddGlobalOption(betaOption);
rootCommand.SetHandler(a => { alphas = a; }, alphaOption);
rootCommand.SetHandler(b => { betas = b; }, betaOption);
await RunInvokeAsync(rootCommand, args);
await RunInvokeAsync(rootCommand, "-a", "1234", "5678");
}
private static async Task RunInvokeAsync(RootCommand rootCommand, params string[] args)
{
int status = await rootCommand.InvokeAsync(args);
Console.WriteLine($"args: {JsonSerializer.Serialize(args)}, status: {status}, alphas: {JsonSerializer.Serialize(alphas)}, betas: {JsonSerializer.Serialize(betas)}");
}
The output shows that the alphas
values are not really picked up from the command line and thus it stays at the default value of 13579:
args: [], status: 0, alphas: [13579], betas: [24680]
args: ["-a","1234","5678"], status: 0, alphas: [13579], betas: [24680]
Try this
Option<long[]> alphaOption = new
(
aliases: new[] { "-a", "--alpha" },
getDefaultValue: () => new[] { DEFAULT_ALPHA_VALUE },
description: "Numerical alpha values"
)
{
AllowMultipleArgumentsPerToken = true,
};
Option<long[]> betaOption = new
(
aliases: new[] { "-b", "--beta" },
getDefaultValue: () => new[] { DEFAULT_BETA_VALUE },
description: "Numerical beta values"
)
{
AllowMultipleArgumentsPerToken = true,
};
The point of interest is a AllowMultipleArgumentsPerToken = true
lines.
And then register these options as follows
RootCommand rootCommand = new("A test app for multiple numerical values option");
rootCommand.AddGlobalOption(alphaOption);
rootCommand.AddGlobalOption(betaOption);
rootCommand.SetHandler((a,b) =>
{
alphas = a;
betas = b;
}, alphaOption, betaOption);
In this case the following values will be handled correctly
await RunInvokeAsync(rootCommand, "-a", "1234");
await RunInvokeAsync(rootCommand, "-a", "1234", "5678");
await RunInvokeAsync(rootCommand, "-a", "1234", "5678", "-b", "1111", "2222");
The latter two will still fail, but it's expected as you are trying to use "1234 5678" or "1234,5678" as a single argument, which is obviously wrong and can't be parsed as number. To emulate such a cases from the command line, the user should use additional escaping the explicitly tell the interpreter to treat input as a single parameter, so it should not be a problem for you as developer.