Commandline: MapResult Usage with async methods

Created on 7 Apr 2018  路  9Comments  路  Source: commandlineparser/commandline

I'm using it CommandLineParser from and async Task Main method.

I would like to use verbs, so I would use the recommended syntax:

CommandLine.Parser.Default.ParseArguments<Options1, Options2, Options3>(args)
    .MapResult(
      (Options1opts) => Method1(opts),
      (Options2opts) => Method2(opts),
      (Options3opts) => Method2(opts),
      errs => 1);

But in my case, the methods are async. Howo do I use the MapResult in this scenario?
The same problem occurs with the WithParsed methods.

Thanks!

Most helpful comment

@shravan2x I'm not sure I understand. OP's methods return tasks already so there's no issue. Here is a full example of what I'm talking about.

async Task Main()
{
    var args = "a --name tim".Split();
    await CommandLine.Parser.Default.ParseArguments<Options1, Options2, Options3>(args)
        .MapResult(
      (Options1 opts) => Method1(opts),
      (Options2 opts) => Method2(opts),
      (Options3 opts) => Method3(opts),
      errs => Task.FromResult(0));
}

public async Task<int> Method1(Options1 opt)
{
    Console.WriteLine("Method1 " + opt.Name);
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 1;
}

public async Task<int> Method2(Options2 opt)
{
    Console.WriteLine("Method2");
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 2;
}

public async Task<int> Method3(Options3 opt)
{
    Console.WriteLine("Method3");
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 3;
}

[Verb("a")]
public class Options1
{
    [Option('n', "name")]
    public string Name { get; set; }
}

[Verb("b")]
public class Options2
{
}

[Verb("c")]
public class Options3 
{
}

All 9 comments

Can you use await?

.MapResult(
  (Options1opts) => await Method1(opts),
  (Options2opts) => await Method2(opts),

@ericnewton76 It doesn't work, this doesn't await the method. I think it's happening what is explained here:

https://blogs.msdn.microsoft.com/pfxteam/2012/02/08/potential-pitfalls-to-avoid-when-passing-around-async-lambdas/

Take a look to the part when he says "With this function, if I then run the following code:".

It looks only by using await in the lambdas, won't work. The MapResult method isn't returning a Task you can await it, and the Main method will continue its execution before anything inside MapResult is executed.

@SuperJMN since your methods are async, you also need to await the result from the parser:

await CommandLine.Parser.Default.ParseArguments [...]

Taking the timer example you posted, here is how I reworked it to support an async Task Main method - you can probably do the same for your methods, as needed.

async Task Main()
{
    Func<Task> t = async () =>
    {
        await Task.Delay(1000);
    };
    double secs = await Time(t);  // Note we're awaiting the result of the timer method, to return control back to the entry point
    Console.WriteLine("Seconds: {0:F7}", secs);
}

public static async Task<double> Time(Func<Task> action, int iters = 10)
{
    var sw = Stopwatch.StartNew();
    for (int i = 0; i < iters; i++) await action();
    return sw.Elapsed.TotalSeconds / iters;
}

@nemec The result from the parser can be awaited only if it returns a Task, which it doesn't. An overload would help here.

@shravan2x I'm not sure I understand. OP's methods return tasks already so there's no issue. Here is a full example of what I'm talking about.

async Task Main()
{
    var args = "a --name tim".Split();
    await CommandLine.Parser.Default.ParseArguments<Options1, Options2, Options3>(args)
        .MapResult(
      (Options1 opts) => Method1(opts),
      (Options2 opts) => Method2(opts),
      (Options3 opts) => Method3(opts),
      errs => Task.FromResult(0));
}

public async Task<int> Method1(Options1 opt)
{
    Console.WriteLine("Method1 " + opt.Name);
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 1;
}

public async Task<int> Method2(Options2 opt)
{
    Console.WriteLine("Method2");
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 2;
}

public async Task<int> Method3(Options3 opt)
{
    Console.WriteLine("Method3");
    await Task.Delay(TimeSpan.FromSeconds(1));
    return 3;
}

[Verb("a")]
public class Options1
{
    [Option('n', "name")]
    public string Name { get; set; }
}

[Verb("b")]
public class Options2
{
}

[Verb("c")]
public class Options3 
{
}

This issue should be closed. Works perfectly with async/await as long as you're well aware of how async/await works.

Agreed, however I like to give the original submitter time to respond in case they have other problems with the solution.

@nemec I hadn't noticed that MapResult returns a generic TResult. Your example works.

Thanks for the info! Now I know how to proceed :)

Was this page helpful?
0 / 5 - 0 ratings