zz from MSDN: .net 4.5 - Async returns types an...

You can find more formal discussion on the Async and Await here in this link. 

Control Flow in Async Programs (C# and Visual Basic)

You can write and maintain asynchronous programs more easily by using the Async and Await keywords. However, the results might surprise you if you don't understand how your program operates. This topic traces the flow of control through a simple async program to show you when control moves from one method to another and what information is transferred each time.

Note Note

The Async and Await keywords were introduced in Visual Studio 2012. For more information about new features in that version, see What's New in Visual Studio 2012.

In general, you mark methods that contain asynchronous code with the Async (Visual Basic) or async (C#) modifier. In a method that's marked with an async modifier, you can use an Await (Visual Basic) or await (C#) operator to specify where the method pauses to wait for a called asynchronous process to complete. For more information, see Asynchronous Programming with Async and Await (C# and Visual Basic).

The following example uses async methods to download the contents of a specified website as a string and to display the length of the string. The example contains the following two methods.

  • startButton_Click, which calls AccessTheWebAsync and displays the result.

  • AccessTheWebAsync, which downloads the contents of a website as a string and returns the length of the string. AccessTheWebAsync uses an asynchronous HttpClient method, GetStringAsync(String), to download the contents.

Numbered display lines appear at strategic points throughout the program to help you understand how the program runs and to explain what happens at each point that is marked. The display lines are labeled "ONE" through "SIX." The labels represent the order in which the program reaches these lines of code.

The following code shows an outline of the program.

C#
VB
public partial class MainWindow : Window
{  // . . . private async void startButton_Click(object sender, RoutedEventArgs e)
    {  // ONE Task<int> getLengthTask = AccessTheWebAsync();  // FOUR  int contentLength = await getLengthTask;  // SIX resultsTextBox.Text +=
            String.Format("\r\nLength of the downloaded string: {0}.\r\n", contentLength);
    } async Task<int> AccessTheWebAsync()
    {  // TWO HttpClient client = new HttpClient();
        Task<string> getStringTask =
            client.GetStringAsync("http://msdn.microsoft.com");  // THREE   string urlContents = await getStringTask;  // FIVE  return urlContents.Length;
    }
}

Each of the labeled locations, "ONE" through "SIX," displays information about the current state of the program. The following output is produced.

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

You can download the code that this topic uses from MSDN, or you can build it yourself.

Note Note

To run the example, you must have Visual Studio 2012, Visual Studio Express 2012, or the .NET Framework 4.5 installed on your computer.

The first two display lines trace the path as startButton_Click calls AccessTheWebAsync, and AccessTheWebAsync calls the asynchronousHttpClient method GetStringAsync(String). The following image outlines the calls from method to method.

Steps ONE and TWO

The return type of both AccessTheWebAsync and client.GetStringAsync is Task<TResult>. For AccessTheWebAsync, TResult is an integer. ForGetStringAsync, TResult is a string. For more information about async method return types, see Async Return Types (C# and Visual Basic).

A task-returning async method returns a task instance when control shifts back to the caller. Control returns from an async method to its caller either when an Await or await operator is encountered in the called method or when the called method ends. The display lines that are labeled "THREE" through "SIX" trace this part of the process.

In AccessTheWebAsync, the asynchronous method GetStringAsync(String) is called to download the contents of the target webpage. Control returns from client.GetStringAsync to AccessTheWebAsync when client.GetStringAsync returns.

The client.GetStringAsync method returns a task of string that’s assigned to the getStringTask variable in AccessTheWebAsync. The following line in the example program shows the call to client.GetStringAsync and the assignment.

C#
VB
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

You can think of the task as a promise by client.GetStringAsync to produce an actual string eventually. In the meantime, ifAccessTheWebAsync has work to do that doesn't depend on the promised string from client.GetStringAsync, that work can continue whileclient.GetStringAsync waits. In the example, the following lines of output, which are labeled "THREE,” represent the opportunity to do independent work

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

The following statement suspends progress in AccessTheWebAsync when getStringTask is awaited.

C#
VB
string urlContents = await getStringTask;

The following image shows the flow of control from client.GetStringAsync to the assignment to getStringTask and from the creation ofgetStringTask to the application of an await operator.

Step THREE

The await expression suspends AccessTheWebAsync until client.GetStringAsync returns. In the meantime, control returns to the caller ofAccessTheWebAsyncstartButton_Click.

NoteNote

Typically, you await the call to an asynchronous method immediately. For example, one of the following assignments could replace the previous code that creates and then awaits getStringTask:

  • Visual Basic: Dim urlContents As String = Await client.GetStringAsync("http://msdn.microsoft.com")

  • C#: string urlContents = await client.GetStringAsync("http://msdn.microsoft.com");

In this topic, the await operator is applied later to accommodate the output lines that mark the flow of control through the program.

The declared return type of AccessTheWebAsync is Task(Of Integer) in Visual Basic and Task<int> in C#. Therefore, whenAccessTheWebAsync is suspended, it returns a task of integer to startButton_Click. You should understand that the returned task isn’tgetStringTask. The returned task is a new task of integer that represents what remains to be done in the suspended method,AccessTheWebAsync. The task is a promise from AccessTheWebAsync to produce an integer when the task is complete.

The following statement assigns this task to the getLengthTask variable.

C#
VB
Task<int> getLengthTask = AccessTheWebAsync();

As in AccessTheWebAsyncstartButton_Click can continue with work that doesn’t depend on the results of the asynchronous task (getLengthTask) until the task is awaited. The following output lines represent that work.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

Progress in startButton_Click is suspended when getLengthTask is awaited. The following assignment statement suspendsstartButton_Click until AccessTheWebAsync is complete.

C#
VB
int contentLength = await getLengthTask;

In the following illustration, the arrows show the flow of control from the await expression in AccessTheWebAsync to the assignment of a value to getLengthTask, followed by normal processing in startButton_Click until getLengthTask is awaited.

Step FOUR

When client.GetStringAsync signals that it’s complete, processing in AccessTheWebAsync is released from suspension and can continue past the await statement. The following lines of output represent the resumption of processing.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

The operand of the return statement, urlContents.Length, is stored in the task that AccessTheWebAsync returns. The await expression retrieves that value from getLengthTask in startButton_Click.

The following image shows the transfer of control after client.GetStringAsync (and getStringTask) are complete.

Step FIVE

AccessTheWebAsync runs to completion, and control returns to startButton_Click, which is awaiting the completion.

When AccessTheWebAsync signals that it’s complete, processing can continue past the await statement in startButton_Async. In fact, the program has nothing more to do.

The following lines of output represent the resumption of processing in startButton_Async:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

The await expression retrieves from getLengthTask the integer value that’s the operand of the return statement in AccessTheWebAsync. The following statement assigns that value to the contentLength variable.

C#
VB
int contentLength = await getLengthTask;

The following image shows the return of control from AccessTheWebAsync to startButton_Click.

Step SIX


Async Return Types (C# and Visual Basic)

Async methods have three possible return types: Task<TResult>Task, and void. In Visual Basic, the void return type is written as a Sub procedure. For more information about async methods, see Asynchronous Programming with Async and Await (C# and Visual Basic).

Each return type is examined in one of the following sections, and you can find a full example that uses all three types at the end of the topic.

This topic contains the following sections.

The Task<TResult> return type is used for an async method that contains a Return (Visual Basic) or return (C#) statement in which the operand has type TResult.

In the following example, the TaskOfT_MethodAsync async method contains a return statement that returns an integer. Therefore, the method declaration must specify a return type of Task(Of Integer) in Visual Basic or Task<int> in C#.

C#
VB
// TASK<T> EXAMPLE async Task<int> TaskOfT_MethodAsync()
{ // The body of the method is expected to contain an awaited asynchronous  // call.  // Task.FromResult is a placeholder for actual work that returns a string.  var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString()); // The method then can process the result in some way.  int leisureHours; if (today.First() == 'S')
        leisureHours = 16; else leisureHours = 5; // Because the return statement specifies an operand of type int, the  // method must have a return type of Task<int>.  return leisureHours;
}

When TaskOfT_MethodAsync is called from within an await expression, the await expression retrieves the integer value (the value ofleisureHours) that's stored in the task that's returned by TaskOfT_MethodAsync. For more information about await expressions, see Await Operator (Visual Basic) or await (C# Reference).

The following code calls and awaits method TaskOfT_MethodAsync. The result is assigned to the result1 variable.

C#
VB
// Call and await the Task<T>-returning async method in the same statement.  int result1 = await TaskOfT_MethodAsync();

You can better understand how this happens by separating the call to TaskOfT_MethodAsync from the application of Await or await, as the following code shows. A call to method TaskOfT_MethodAsync that isn't immediately awaited returns a Task(Of Integer) or Task<int>, as you would expect from the declaration of the method. The task is assigned to the integerTask variable in the example. Because integerTask is aTask<TResult>, it contains a Result property of type TResult. In this case, TResult represents an integer type. When Await or await is applied tointegerTask, the await expression evaluates to the contents of the Result property of integerTask. The value is assigned to the result2 variable.

Caution note Caution

The Result property is a blocking property. If you try to access it before its task is finished, the thread that's currently active is blocked until the task completes and the value is available. In most cases, you should access the value by using Await or await instead of accessing the property directly.

C#
VB
// Call and await in separate statements. Task<int> integerTask = TaskOfT_MethodAsync(); // You can do other work that does not rely on integerTask before awaiting. textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n"); int result2 = await integerTask;

The display statements in the following code verify that the values of the result1 variable, the result2 variable, and the Result property are the same. Remember that the Result property is a blocking property and shouldn't be accessed before its task has been awaited.

C#
VB
// Display the values of the result1 variable, the result2 variable, and  // the integerTask.Result property. textBox1.Text += String.Format("\r\nValue of result1 variable:   {0}\r\n", result1);
textBox1.Text += String.Format("Value of result2 variable:   {0}\r\n", result2);
textBox1.Text += String.Format("Value of integerTask.Result: {0}\r\n", integerTask.Result);

Async methods that don't contain a return statement or that contain a return statement that doesn't return an operand usually have a return type ofTask. Such methods would be void-returning methods (Sub procedures in Visual Basic) if they were written to run synchronously. If you use a Taskreturn type for an async method, a calling method can use an await operator to suspend the caller's completion until the called async method has finished.

In the following example, async method Task_MethodAsync doesn't contain a return statement. Therefore, you specify a return type of Task for the method, which enables Task_MethodAsync to be awaited. The definition of the Task type doesn't include a Result property to store a return value.

C#
VB
// TASK EXAMPLE async Task Task_MethodAsync()
{ // The body of an async method is expected to contain an awaited   // asynchronous call.  // Task.Delay is a placeholder for actual work. await Task.Delay(2000); // Task.Delay delays the following line by two seconds. textBox1.Text += String.Format("\r\nSorry for the delay. . . .\r\n"); // This method has no return statement, so its return type is Task.  }

Task_MethodAsync is called and awaited by using an await statement instead of an await expression, similar to the calling statement for a synchronous Sub or void-returning method. The application of an await operator in this case doesn't produce a value.

The following code calls and awaits method Task_MethodAsync.

C#
VB
// Call and await the Task-returning async method in the same statement. await Task_MethodAsync();

As in the previous Task<TResult> example, you can separate the call to Task_MethodAsync from the application of an await operator, as the following code shows. However, remember that a Task doesn't have a Result property, and that no value is produced when an await operator is applied to a Task.

The following code separates calling Task_MethodAsync from awaiting the task that Task_MethodAsync returns.

C#
VB
// Call and await in separate statements. Task simpleTask = Task_MethodAsync(); // You can do other work that does not rely on simpleTask before awaiting. textBox1.Text += String.Format("\r\nApplication can continue working while the Task runs. . . .\r\n"); await simpleTask;

The primary use of the void return type (Sub procedures in Visual Basic) is in event handlers, where a void return type is required. A void return also can be used to override void-returning methods or for methods that perform activities that can be categorized as "fire and forget." However, you should return a Task wherever possible, because a void-returning async method can't be awaited. Any caller of such a method must be able to continue to completion without waiting for the called async method to finish, and the caller must be independent of any values or exceptions that the async method generates.

The caller of a void-returning async method can't catch exceptions that are thrown from the method, and such unhandled exceptions are likely to cause your application to fail. If an exception occurs in an async method that returns a Task or Task<TResult>, the exception is stored in the returned task, and rethrown when the task is awaited. Therefore, make sure that any async method that can produce an exception has a return type of Task or Task<TResult> and that calls to the method are awaited.

For more information about how to catch exceptions in async methods, see try-catch (C# Reference) or Try...Catch...Finally Statement (Visual Basic).

The following code defines an async event handler.

C#
VB
// VOID EXAMPLE  private async void button1_Click(object sender, RoutedEventArgs e)
{
    textBox1.Clear(); // Start the process and await its completion. DriverAsync is a   // Task-returning async method. await DriverAsync(); // Say goodbye. textBox1.Text += "\r\nAll done, exiting button-click event handler.";
}

The following Windows Presentation Foundation (WPF) project contains the code examples from this topic.

To run the project, perform the following steps:

  1. Start Visual Studio.

  2. On the menu bar, choose FileNewProject.

    The New Project dialog box opens.

  3. In the InstalledTemplates category, choose Visual Basic or Visual C#, and then choose Windows. Choose WPF Application from the list of project types.

  4. Enter AsyncReturnTypes as the name of the project, and then choose the OK button.

    The new project appears in Solution Explorer.

  5. In the Visual Studio Code Editor, choose the MainWindow.xaml tab.

    If the tab is not visible, open the shortcut menu for MainWindow.xaml in Solution Explorer, and then choose Open.

  6. In the XAML window of MainWindow.xaml, replace the code with the following code.

    C#
    VB
    <Window x:Class="AsyncReturnTypes.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525">
        <Grid>
            <Button x:Name="button1" Content="Start" HorizontalAlignment="Left" Margin="214,28,0,0" VerticalAlignment="Top" Width="75" HorizontalContentAlignment="Center" FontWeight="Bold" FontFamily="Aharoni" Click="button1_Click"/>
            <TextBox x:Name="textBox1" Margin="0,80,0,0" TextWrapping="Wrap" FontFamily="Lucida Console"/>
    
        </Grid>
    </Window>

    A simple window that contains a text box and a button appears in the Design window of MainWindow.xaml.

  7. In Solution Explorer, open the shortcut menu for MainWindow.xaml.vb or MainWindow.xaml.cs, and then choose View Code.

  8. Replace the code in MainWindow.xaml.vb or MainWindow.xaml.cs with the following code.

    C#
    VB
    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace AsyncReturnTypes
    { public partial class MainWindow : Window
        { public MainWindow()
            {
                InitializeComponent();
            } // VOID EXAMPLE  private async void button1_Click(object sender, RoutedEventArgs e)
            {
                textBox1.Clear(); // Start the process and await its completion. DriverAsync is a   // Task-returning async method. await DriverAsync(); // Say goodbye. textBox1.Text += "\r\nAll done, exiting button-click event handler.";
            } async Task DriverAsync()
            { // Task<T>   // Call and await the Task<T>-returning async method in the same statement.  int result1 = await TaskOfT_MethodAsync(); // Call and await in separate statements. Task<int> integerTask = TaskOfT_MethodAsync(); // You can do other work that does not rely on integerTask before awaiting. textBox1.Text += String.Format("Application can continue working while the Task<T> runs. . . . \r\n"); int result2 = await integerTask; // Display the values of the result1 variable, the result2 variable, and  // the integerTask.Result property. textBox1.Text += String.Format("\r\nValue of result1 variable:   {0}\r\n", result1);
                textBox1.Text += String.Format("Value of result2 variable:   {0}\r\n", result2);
                textBox1.Text += String.Format("Value of integerTask.Result: {0}\r\n", integerTask.Result); // Task  // Call and await the Task-returning async method in the same statement. await Task_MethodAsync(); // Call and await in separate statements. Task simpleTask = Task_MethodAsync(); // You can do other work that does not rely on simpleTask before awaiting. textBox1.Text += String.Format("\r\nApplication can continue working while the Task runs. . . .\r\n"); await simpleTask;
            } // TASK<T> EXAMPLE async Task<int> TaskOfT_MethodAsync()
            { // The body of the method is expected to contain an awaited asynchronous  // call.  // Task.FromResult is a placeholder for actual work that returns a string.  var today = await Task.FromResult<string>(DateTime.Now.DayOfWeek.ToString()); // The method then can process the result in some way.  int leisureHours; if (today.First() == 'S')
                    leisureHours = 16; else leisureHours = 5; // Because the return statement specifies an operand of type int, the  // method must have a return type of Task<int>.  return leisureHours;
            } // TASK EXAMPLE async Task Task_MethodAsync()
            { // The body of an async method is expected to contain an awaited   // asynchronous call.  // Task.Delay is a placeholder for actual work. await Task.Delay(2000); // Task.Delay delays the following line by two seconds. textBox1.Text += String.Format("\r\nSorry for the delay. . . .\r\n"); // This method has no return statement, so its return type is Task.  }
        }
    }
  9. Choose the F5 key to run the program, and then choose the Start button.

    The following output should appear.

    Application can continue working while the Task<T> runs. . . . 
    
    Value of result1 variable:   5
    Value of result2 variable:   5
    Value of integerTask.Result: 5
    
    Sorry for the delay. . . .
    
    Application can continue working while the Task runs. . . .
    
    Sorry for the delay. . . .
    
    All done, exiting button-click event handler.

Resources: 

Async sample: Example from "Asynchronous Programming with Async and Await": http://code.msdn.microsoft.com/Async-Sample-Example-from-9b9f505c
Asynchronous Programming with "Async" and "Await": http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx
Control flow in Async Operation: http://msdn.microsoft.com/en-us/library/vstudio/hh873191.aspx

转载于:https://my.oschina.net/u/854138/blog/103439

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值