Why do some async methods require a return type of

2019-06-07 05:39发布

问题:

In this example from Microsoft, the method has a return type of Task<int>

Example 1:

async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the 
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync. 
    //  - AccessTheWebAsync can't continue until getStringTask is complete. 
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync. 
    //  - Control resumes here when getStringTask is complete.  
    //  - The await operator then retrieves the string result from getStringTask. 
    string urlContents = await getStringTask;

    // The return statement specifies an integer result. 
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value. 
    return urlContents.Length;
}

In this second example, it uses async and await, BUT doesn't return a type of Task<>, why?

Example 2:

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            ReadCharacters();
        }

        static async void ReadCharacters()
        {
            String result;
            using (StreamReader reader = File.OpenText("existingfile.txt"))
            {
                Console.WriteLine("Opened file.");
                result = await reader.ReadToEndAsync();
                Console.WriteLine("Contains: " + result);
            }
        }
    }
}

Third, in the first example, is it possible to return an array(strings)?

回答1:

In this second example, it uses async and await, BUT doesn't return a type of Task<>, why?

They made an error. Whenever you create a method which is async and doesn't have a return value, it should return a Task. The only exception to that is event handlers, where you need to keep compatibility with the delegates signature, but this isn't the case. Think of a Task as the void equivalent of asynchronous methods.

Why do you actually want to return a Task and not void? Because returning a Task allows you to monitor the status of execution, and also lets you to properly handle any exceptions encapsulated inside the on going operation.

For example, think of an async void method that throws:

public async void WaitAndThrowAsync()
{
    await Task.Delay(1000);
    throw new Exception("yay");
}

public void CallWaitAndThrowAsync()
{
    // What happens when it throws here?
    WaitAndThrowAsync();
}

When you invoke this, you have no way to actually handle exceptions happening inside the method, it is "fire and forget" for the call-site. But when you expose a Task, you can now better handle that exception by asynchronously waiting:

public async Task WaitAndThrowAsync()
{
    await Task.Delay(1000);
    throw new Exception("yay");
}

public async Task CallWaitAndThrowAsync()
{
    try
    {
        await WaitAndThrowAsync(); 
    }
    catch (Exception e)
    {
       // Do something. 
    }
}

Third, in the first example, is it possible to return an array(strings)?

Yes, by returning a Task<string[]>:

public async Task<string> GetArrayAsync()
{
    HttpClient client = new HttpClient();
    var responseStream = await client.GetStreamAsync("http://msdn.microsoft.com");

    using (var streamReader = new StreamReader(responseStream))
    {
        return await streamReader.ReadToEndAsync();
    }
}

When you mark a method async, the compiler will implicitly create a Task for you. When you have a return type, the generated task is a Task<T> where T is your return type.



回答2:

The second example is not a good example: with async void it's not possible anymore to await the asynchronous method. async void should be used for event handlers only.

For your second question, yes it's possible to return an array of strings. Just use Task<string[]>.

I really recommend you to read this excellent article from Stephen Cleary: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Some best practices from the article:

  • Avoid async void: Prefer async Task methods over async void methods. Exceptions: Event handlers
  • Async all the way: Don’t mix blocking and async code. Exceptions: Console main method


回答3:

That second isn't according to the guidelines specified by Microsoft:

The return type is one of the following types: - Task if your method has a return statement in which the operand has type TResult. - Task if your method has no return statement or has a return statement with no operand. - Void (a Sub in Visual Basic) if you're writing an async event handler.

Why should methods return Task or Task<T>?

Each returned task represents ongoing work. A task encapsulates information about the state of the asynchronous process and, eventually, either the final result from the process or the exception that the process raises if it doesn't succeed.

So you lose information when you use void. You discard the actual task progress and execution information. So for the void return type, use Task. For any other type, use Task<T>, where T is the actual return type.

Some methods can return void, but that is prohibited to event handler or starter methods:

An async method can also be a Sub method (Visual Basic) or have a void return type (C#). This return type is used primarily to define event handlers, where a void return type is required. Async event handlers often serve as the starting point for async programs.