Асинхронные типы возвращаемых значений (C# и Visual Basic)

Асинхронные методы имеют три возможных типа возвращаемых значений: Task, Task и void. В Visual Basic тип возвращаемого значения void записывается как процедура Sub. Дополнительные сведения об асинхронных методах см. в разделе Асинхронное программирование с использованием ключевых слов Async и Await (C# и Visual Basic).

Каждый тип возвращаемого значения рассматривается в одном из следующих подразделов. Полный пример, в котором используются все три типа, можно найти в конце данного раздела.

Примечание

Для запуска примера необходимо, чтобы на компьютере была установлена Visual Studio 2012, Visual Studio 2013, Visual Studio Express 2012 для Windows Desktop, Visual Studio Express 2013 для Windows или .NET Framework 4.5 или 4.5.1.

В этом разделе содержатся следующие подразделы.

  • Возвращаемый тип Task(T)
  • Возвращаемый тип задачи
  • Тип возвращаемого значения void
  • Полный пример
  • Связанные разделы

Возвращаемый тип Task(T)

Возвращаемый тип Task используется для асинхронного метода, содержащего оператор Return (Visual Basic) или return (C#), в котором операнд имеет тип TResult.

В следующем примере асинхронный метод TaskOfT_MethodAsync содержит оператор return, который возвращает целое число. Поэтому, объявление метода должно указывать тип возвращаемого значения Task(Of Integer) в Visual Basic или Task<int> в C#.

' TASK(OF T) EXAMPLE
Async Function TaskOfT_MethodAsync() As Task(Of Integer)

    ' The body of an async method is expected to contain an awaited  
    ' asynchronous call. 
    ' Task.FromResult is a placeholder for actual work that returns a string. 
    Dim today As String = Await Task.FromResult(Of String)(DateTime.Now.DayOfWeek.ToString())

    ' The method then can process the result in some way. 
    Dim leisureHours As Integer 
    If today.First() = "S" Then
        leisureHours = 16
    Else
        leisureHours = 5
    End If 

    ' Because the return statement specifies an operand of type Integer, the  
    ' method must have a return type of Task(Of Integer).  
    Return leisureHours
End Function
// 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;
}

При вызове TaskOfT_MethodAsync из выражения await, выражение await извлекает целое число (значение leisureHours), которое хранится в задаче, возвращаемой TaskOfT_MethodAsync. Дополнительные сведения о выражениях await см. в разделе Оператор Await (Visual Basic) или await (Справочник по C#).

Следующий код вызывает и ожидает метод TaskOfT_MethodAsync. Результат присваивается переменной result1.

' Call and await the Task(Of T)-returning async method in the same statement. 
Dim result1 As Integer = Await TaskOfT_MethodAsync()
// Call and await the Task<T>-returning async method in the same statement. 
int result1 = await TaskOfT_MethodAsync();

Лучше понять, как это происходит, можно, разделив вызов TaskOfT_MethodAsync из приложения Await или await, как показано в следующем коде. Вызов метода TaskOfT_MethodAsync, который невозможно ожидать сразу, возвращает Task(Of Integer) или Task<int>, как ожидалось из объявления метода. Задача присваивается переменной integerTask в этом примере. Поскольку integerTask является Task, она содержит свойство Result типа TResult. В этом случае TResult представляет целочисленный тип. Когда Await или await применяется к integerTask, выражение await оценивается как содержимое свойства Result переменной integerTask. Значение присваивается переменной result2.

Предупреждение

Свойство Result является свойством блокировки.При попытке доступа к нему до завершения его задачи поток, активный в настоящий момент, блокируется до тех пор, пока задача не будет завершена и значение не станет доступно.В большинстве случаев вместо непосредственного обращения к свойству доступ к значению можно получить, используя Await или await.

' Call and await in separate statements. 
Dim integerTask As Task(Of Integer) = TaskOfT_MethodAsync()

' You can do other work that does not rely on resultTask before awaiting.
textBox1.Text &= String.Format("Application can continue working while the Task(Of T) runs. . . . " & vbCrLf)

Dim result2 As Integer = Await integerTask
// 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;

Операторы отображения в следующем коде проверяют, что значения переменной result1, переменной result2 и свойства Result одинаковы. Помните, что свойство Result является блокирующим свойством и к нему не стоит обращаться до того, как его задача будет подождана.

' Display the values of the result1 variable, the result2 variable, and 
' the resultTask.Result property.
textBox1.Text &= String.Format(vbCrLf & "Value of result1 variable:   {0}" & vbCrLf, result1)
textBox1.Text &= String.Format("Value of result2 variable:   {0}" & vbCrLf, result2)
textBox1.Text &= String.Format("Value of resultTask.Result:  {0}" & vbCrLf, integerTask.Result)
// 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);

Возвращаемый тип задачи

Асинхронные методы, не содержащие оператора return или содержащие оператор return, который не возвращает операнд, обычно имеют тип возвращаемого значения Task. Такие методы были бы методами, возвращающими void (процедурами Sub в Visual Basic), если бы они были написаны так, чтобы выполняться синхронно. При использовании возвращаемого типа Task для асинхронного метода вызывающий метод может использовать оператор await для приостановки выполнения вызывающего объекта до тех пор, пока асинхронный метод не завершится.

В следующем примере асинхронный метод Task_MethodAsync не содержит оператора return. Поэтому необходимо указывать возвращаемый тип Task для метода, который включает Task_MethodAsync для ожидания. Определение типа Task не содержит свойства Result для хранения возвращаемого значения.

' TASK EXAMPLE
Async Function Task_MethodAsync() As Task

    ' 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)
    textBox1.Text &= String.Format(vbCrLf & "Sorry for the delay. . . ." & vbCrLf)

    ' This method has no return statement, so its return type is Task.  
End Function
// 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 вызывается и ожидается с помощью оператора await вместо выражения await, похожего на оператор вызова для синхронного Sub или возвращающего void метода. Приложение оператора await в этом случае не возвращает значение.

Следующий код вызывает и ожидает метод Task_MethodAsync.

' Call and await the Task-returning async method in the same statement.
Await Task_MethodAsync()
// Call and await the Task-returning async method in the same statement.
await Task_MethodAsync();

Как и в предыдущем примере Task, можно отделить вызов Task_MethodAsync от приложения оператора await, как показано в следующем коде. Однако следует помнить, что Task не имеет свойства Result и при применении оператора await к Task никакое значение не создается.

Следующий код отделяет вызов Task_MethodAsync от ожидания задачи, которую возвращает Task_MethodAsync.

' Call and await in separate statements. 
Dim simpleTask As Task = Task_MethodAsync()

' You can do other work that does not rely on simpleTask before awaiting.
textBox1.Text &= String.Format(vbCrLf & "Application can continue working while the Task runs. . . ." & vbCrLf)

Await simpleTask
// 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;

Тип возвращаемого значения void

Основное использование возвращаемого значения void (процедуры Sub в Visual Basic) — в обработчиках событий, где требуется возвращать значение типа void. Возврат void также можно использовать для переопределения возвращающих void методов или для методов, выполняющих действия, которые подчиняются принципу "отправить и забыть". Однако необходимо возвращать Task всегда, когда это возможно, поскольку асинхронные методы, возвращающие void, ожидать невозможно. Любой вызывающий объект такого метода должен иметь возможность продолжить работу до завершения, не дожидаясь завершения вызванного асинхронного метода, и он не должен зависеть ни от каких значений и исключений, создаваемых асинхронным методом.

Вызывающий объект асинхронного метода, возвращающего void, не может перехватывать исключения, созданные из этого метода, и такие необработанные исключения могут привести к сбою приложения. Если исключение возникает в асинхронном методе, возвращающем Task или Task, оно сохраняется в возвращенной задаче и создается повторно, когда задача ожидается. Поэтому убедитесь в том, что любой асинхронный метод, который может создавать исключение, имеет возвращаемый тип Task или Task, и что вызовы этого метода ожидаются.

Дополнительные сведения о том, как перехватывать исключения в асинхронных методах, см. в разделе try-catch (Справочник по C#) или Оператор Try... Catch... Finally (Visual Basic).

Следующий код определяет асинхронный обработчик события.

' SUB EXAMPLE
Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click

    textBox1.Clear()

    ' Start the process and await its completion. DriverAsync is a  
    ' Task-returning async method.
    Await DriverAsync()

    ' Say goodbye.
    textBox1.Text &= vbCrLf & "All done, exiting button-click event handler." 
End Sub
// 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.";
}

Полный пример

Следующий проект Windows Presentation Foundation (WPF) содержит пример кода из данного раздела.

Чтобы запустить этот проект, выполните указанные ниже действия:

  1. Запустите Visual Studio.

  2. В меню Файл выберите Создать, Проект.

    Откроется диалоговое окно Новый проект.

  3. В категории Установленные, Шаблоны выберите Visual Basic или Visual C# и выберите Windows. В списке типов проектов выберите Приложение WPF.

  4. Введите в качестве имени проекта AsyncReturnTypes, а затем нажмите кнопку ОК.

    В обозревателе решений появится новый проект.

  5. Выберите в редакторе кода Visual Studio вкладку MainWindow.xaml.

    Если вкладка не отображается, откройте контекстное меню для MainWindow.xaml в обозревателе решений, а затем выберите Открыть.

  6. В окне XAML MainWindow.xaml замените код на следующий код

    <Window x:Class="MainWindow"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://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>
    
    <Window x:Class="AsyncReturnTypes.MainWindow"
            xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="https://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>
    

    Простое окно, содержащее текстовое поле и кнопку, отображается в окне Конструктор MainWindow.xaml.

  7. В Обозревателе решений откройте контекстное меню элемента MainWindow.xaml.vb или MainWindow.xaml.cs и выберите команду Просмотреть код.

  8. В файле MainWindow.xaml.vb или MainWindow.xaml.cs замените код на следующий.

    Class MainWindow
    
        ' SUB EXAMPLE
        Async Sub button1_Click(sender As Object, e As RoutedEventArgs) Handles button1.Click
    
            textBox1.Clear()
    
            ' Start the process and await its completion. DriverAsync is a  
            ' Task-returning async method.
            Await DriverAsync()
    
            ' Say goodbye.
            textBox1.Text &= vbCrLf & "All done, exiting button-click event handler." 
        End Sub
    
    
        Async Function DriverAsync() As Task
    
            ' Task(Of T)  
            ' Call and await the Task(Of T)-returning async method in the same statement. 
            Dim result1 As Integer = Await TaskOfT_MethodAsync()
    
            ' Call and await in separate statements. 
            Dim integerTask As Task(Of Integer) = TaskOfT_MethodAsync()
    
            ' You can do other work that does not rely on resultTask before awaiting.
            textBox1.Text &= String.Format("Application can continue working while the Task(Of T) runs. . . . " & vbCrLf)
    
            Dim result2 As Integer = Await integerTask
    
            ' Display the values of the result1 variable, the result2 variable, and 
            ' the resultTask.Result property.
            textBox1.Text &= String.Format(vbCrLf & "Value of result1 variable:   {0}" & vbCrLf, result1)
            textBox1.Text &= String.Format("Value of result2 variable:   {0}" & vbCrLf, result2)
            textBox1.Text &= String.Format("Value of resultTask.Result:  {0}" & vbCrLf, integerTask.Result)
    
            ' Task  
            ' Call and await the Task-returning async method in the same statement.
            Await Task_MethodAsync()
    
            ' Call and await in separate statements. 
            Dim simpleTask As Task = Task_MethodAsync()
    
            ' You can do other work that does not rely on simpleTask before awaiting.
            textBox1.Text &= String.Format(vbCrLf & "Application can continue working while the Task runs. . . ." & vbCrLf)
    
            Await simpleTask
        End Function 
    
    
        ' TASK(OF T) EXAMPLE
        Async Function TaskOfT_MethodAsync() As Task(Of Integer)
    
            ' The body of an async method is expected to contain an awaited  
            ' asynchronous call. 
            ' Task.FromResult is a placeholder for actual work that returns a string. 
            Dim today As String = Await Task.FromResult(Of String)(DateTime.Now.DayOfWeek.ToString())
    
            ' The method then can process the result in some way. 
            Dim leisureHours As Integer 
            If today.First() = "S" Then
                leisureHours = 16
            Else
                leisureHours = 5
            End If 
    
            ' Because the return statement specifies an operand of type Integer, the  
            ' method must have a return type of Task(Of Integer).  
            Return leisureHours
        End Function 
    
    
        ' TASK EXAMPLE
        Async Function Task_MethodAsync() As Task
    
            ' 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)
            textBox1.Text &= String.Format(vbCrLf & "Sorry for the delay. . . ." & vbCrLf)
    
            ' This method has no return statement, so its return type is Task.  
        End Function 
    
    End Class
    
    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. Нажмите клавишу F5, чтобы запустить программу, затем нажмите кнопку Start.

    Появятся следующие выходные данные.

    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.
    

См. также

Задачи

Пошаговое руководство. Получение доступа к Интернету с помощью модификатора Async и оператора Await (C# и Visual Basic)

Пошаговое руководство. Использование отладчика с асинхронными методами

Ссылки

async (справочник по C#)

Async (Visual Basic)

Оператор Await (Visual Basic)

await (Справочник по C#)

FromResult``1

Основные понятия

Поток управления в асинхронных программах (C# и Visual Basic)