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

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

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

ПримечаниеПримечание

Запуск образца необходимо иметь Visual Studio 2012, Visual Studio Express 2012 для рабочего стола Windows или .NET Framework 4,5, на компьютере.

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

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

Тип возвращаемого значения Task(T)

Возвращаемый тип Task<TResult> используется для асинхронного метода, содержащего оператор 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 из выражения ожидания, выражение ожидания извлекает целое число (значение 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<TResult>, она содержит свойство Result типа TResult.В этом случае TResult представляет тип integer.Когда Await или await применяется к integerTask, выражение ожидания оценивается как содержимое свойства 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);

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

Асинхронные методы, не содержащие оператора 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<TResult>, можно разделить вызов 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<TResult>, то исключение хранится в возвращенной задаче и создается повторно, когда задача ожидается.Поэтому убедитесь в том, что любой асинхронный метод, который может создавать исключение, имеет возвращаемый тип Task или Task<TResult>, и что вызовы этого метода ожидаются.

Дополнительные сведения о том, как перехватывать исключения в асинхронных методах см. в разделе 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#, а затем пункт Окна.Выберите Приложение WPF из списка типов проектов.

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

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

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

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

  6. В окне 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<TResult>

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

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