C# .NET async await return type

2019-07-24 19:46发布

Just hoping someone could clear up return types when using async and await in C#. I understand that you have three return types being Task<T>, Task and void.

From trying to learn about this I have read you should only use void in special circumstances (Event Handlers) and if you are changing a synchronous method to an async method then you should change a void keyword to Task.

My question is do I need a Task return type even though the code is completely independent and the result doesn't matter for the continuation? Say for example I use the asynchronous method to push some information to a web service.

I have created a small example to help explain:

private void CallingMethod()
{
    AsyncMethod();
    AsyncTaskMethod(); // any difference between doing these? (not exc handling)

    var task = AsyncTaskMethod(); // should I do anything with this task
                                  // before I continue? Or is it simply 
                                  // just to show the user that it's async??

    // continue with unrelated code that doesnt require task or any code from above
}

private async void AsyncMethod() // is void okay?
{
    await LongRunningMethod();

    // do other stuff
}

private async Task AsyncTaskMethod() // or should it be Task?
{
    await LongRunningMethod();

    // do other stuff
}

private async Task LongRunningMethod()
{
    // do long running code
}

My application has a lot of methods like so:

private void BigMethod()
{
    DoStuff();

    DoMoreStuff();

    AsyncMethod(); // is this right? Or should I have the task declaration too?

    UnrelatedStuff();
}

private async Task AsyncMethod() // should this return task?
{
    var result = await GetUserId();

    if (result == null)
        return;

    MyUserId = result;
}

private async Task<int> GetUserId()
{
    // do long running code
    return 1;
}

If it's not clear, by all means just say and I'll try to tidy it up a bit. Thanks guys.

EDIT:

private void CallingMethod()
{
    AsyncMethod();
}

private async void AsyncMethod()
{
    await LongRunningMethod();
}

private async Task LongRunningMethod()
{
    await Task.Run(() =>
    {
        Thread.Sleep(4000);
    });

    MessageBox.Show("Done!");
}

In response to the first comment, the above CallingMethod() does not use async/await and it happily sleeps for 4 seconds without locking up the UI thread, then pops up the message box. What's the problem?

2条回答
三岁会撩人
2楼-- · 2019-07-24 20:13

You don't have to use Task when a method returns void, but it is indeed preferred, for several reasons:

  1. Async methods that return a Task (or Task<T>) have their exceptions put on the returned Task, so calling methods can check (when they see fit) whether the Task was faulted or not.

  2. Async void methods have their exceptions thrown directly on the calling synchronization context, making it nearly (nearly because you can still use global exception handlers, but that's unmaintainable for big applications) impossible to handle exceptions on them on a deterministic manner.

  3. You basically can't test any async void methods that may throw an exception

  4. async void methods are assumed to be complete when they are run by the caller. This can introduce many problems (in fact, there are many problems with event handlers when you try to convert an existing library to async: go ahead, get a third-party relatively complex winforms library and try to make your event handlers async, you'll see the havoc it creates!).

  5. There is no reasonable way to determine when an async void method has finished (you could make your own SynchronizationContext, but that's something definitely complex)

  6. You can't configure an async void call (you can't use ConfigureAwait on it), so the continuation context is always the caller's synchronization context.

So...

TL;DR: don't use async void unless you can't avoid it (that is: with event handlers that expect a void signature). There are reasons for it. Use async Task if you don't expect a result.

查看更多
欢心
3楼-- · 2019-07-24 20:13

You cannot await methods that return void and also one of the main reasons is that the caller of a void-returning async method can't catch exceptions that are thrown from the async method. Try that:

  public void TryToCatchMethod() {
    try {
      MyAsyncMethod();
    } catch (Exception exception) {
      //tha catch is never called
      Debug.WriteLine(exception.ToString());
    }
  }
  public async void MyAsyncMethod() {
    throw new Exception();
  }
查看更多
登录 后发表回答