Walkthrough: Accessing the Web by Using Async and Await (Visual Basic) (Пошаговое руководство. Доступ к веб-сайтам с помощью модификатора Async и оператора Await (Visual Basic))

Возможности Async и Await упрощают создание асинхронных программ. Можно написать асинхронный код, который выглядит как синхронный, и позволить компилятору обрабатывать трудные функции обратного вызова и продолжения, которые обычно включает асинхронный код.

Дополнительные сведения о функции Асинхронного программирования см. в статье "Асинхронное программирование" с помощью Async и Await (Visual Basic).

Это пошаговое руководство начинается с создания синхронного приложения Windows Presentation Foundation (WPF), которое суммирует число байтов в списке веб-сайтов. Затем в рамках руководства приложение преобразуется в асинхронное решение с помощью новых возможностей.

Вы можете разрабатывать приложения, выполнив пошаговое руководство или скачав пример из браузера .NET Sample Browser. Пример кода находится в проекте SerialAsyncExample .

В этом пошаговом руководстве выполняются следующие задачи.

Полный асинхронный пример см. в разделе "Пример ".

Необходимые компоненты

На компьютере должна быть установлена среда Visual Studio 2012 или более поздней версии. Дополнительные сведения см. на странице загрузки Visual Studio.

создание приложения WPF;

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

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

    Откроется диалоговое окно Создание проекта .

  3. В области установленных шаблонов выберите Visual Basic и выберите приложение WPF из списка типов проектов.

  4. В текстовом поле Имя введите AsyncExampleWPF и нажмите кнопку ОК.

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

Разработка простого окна MainWindow WPF

  1. В редакторе кода Visual Studio перейдите на вкладку MainWindow.xaml .

  2. Если окно Панель элементов не отображается, в меню Вид выберите пункт Панель элементов.

  3. Добавьте элементы управления Button и TextBox в окно MainWindow.

  4. Выделите элемент управления TextBox и в окне Свойства задайте указанные ниже значения.

    • Задайте для свойства Имя значение resultsTextBox.

    • Задайте для свойства Высота значение 250.

    • Задайте для свойства Ширина значение 500.

    • На вкладке Текст укажите моноширинный шрифт, например Lucida Console или Global Monospace.

  5. Выделите элемент управления Button и в окне Свойства задайте указанные ниже значения.

    • Задайте для свойства Имя значение startButton.

    • Измените значение свойства Содержимое с Button на Start.

  6. Поместите текстовое поле и кнопку так, чтобы оба элемента управления отображались в окне MainWindow.

    Дополнительные сведения о конструкторе XAML WPF см. в разделе Создание пользовательского интерфейса с помощью конструктора XAML.

Добавление ссылки

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

  2. В строке меню выберите Проект, Добавить ссылку.

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

  3. Вверху диалогового окна убедитесь в том, что проект предназначен для платформы .NET Framework 4.5 или более поздней версии.

  4. В области Сборки выберите Платформа, если этот вариант еще не выбран.

  5. В списке имен установите флажок System.Net.Http.

  6. Нажмите кнопку ОК, чтобы закрыть диалоговое окно.

Добавление необходимых инструкций Import

  1. В Обозреватель решений откройте контекстное меню для MainWindow.xaml.vb, а затем выберите "Просмотреть код".

  2. Добавьте следующие Imports инструкции в верхней части файла кода, если они еще не присутствуют.

    Imports System.Net.Http
    Imports System.Net
    Imports System.IO
    

Создание синхронного приложения

  1. В окне конструктора MainWindow.xaml дважды нажмите кнопку "Пуск ", чтобы создать startButton_Click обработчик событий в MainWindow.xaml.vb.

  2. В MainWindow.xaml.vb скопируйте следующий код в текст startButton_Click:

    resultsTextBox.Clear()
    SumPageSizes()
    resultsTextBox.Text &= vbCrLf & "Control returned to startButton_Click."
    

    Код вызывает метод, который управляет приложением SumPageSizes и выводит на экран сообщение, когда элемент управления возвращается к startButton_Click.

  3. Код для синхронного решения содержит следующие четыре метода:

    • SumPageSizes, который получает список URL-адресов веб-страниц из SetUpURLList, а затем вызывает метод GetURLContents и DisplayResults для обработки каждого URL-адреса;

    • SetUpURLList, который создает и возвращает список веб-адресов;

    • GetURLContents, который загружает содержимое каждого веб-сайта и возвращает содержимое в виде массива байтов;

    • DisplayResults, который показывает число байтов в массиве байтов для каждого URL-адреса.

    Скопируйте следующие четыре метода и вставьте их в startButton_Click обработчик событий в MainWindow.xaml.vb:

    Private Sub SumPageSizes()
    
        ' Make a list of web addresses.
        Dim urlList As List(Of String) = SetUpURLList()
    
        Dim total = 0
        For Each url In urlList
            ' GetURLContents returns the contents of url as a byte array.
            Dim urlContents As Byte() = GetURLContents(url)
    
            DisplayResults(url, urlContents)
    
            ' Update the total.
            total += urlContents.Length
        Next
    
        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "Total bytes returned:  {0}" & vbCrLf, total)
    End Sub
    
    Private Function SetUpURLList() As List(Of String)
    
        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function
    
    Private Function GetURLContents(url As String) As Byte()
    
        ' The downloaded resource ends up in the variable named content.
        Dim content = New MemoryStream()
    
        ' Initialize an HttpWebRequest for the current URL.
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)
    
        ' Send the request to the Internet resource and wait for
        ' the response.
        ' Note: you can't use HttpWebRequest.GetResponse in a Windows Store app.
        Using response As WebResponse = webReq.GetResponse()
            ' Get the data stream that is associated with the specified URL.
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.
                responseStream.CopyTo(content)
            End Using
        End Using
    
        ' Return the result as a byte array.
        Return content.ToArray()
    End Function
    
    Private Sub DisplayResults(url As String, content As Byte())
    
        ' Display the length of each website. The string format
        ' is designed to be used with a monospaced font, such as
        ' Lucida Console or Global Monospace.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub
    

Тестирование синхронного решения

  1. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start .

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

    msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
    msdn.microsoft.com                                            33964
    msdn.microsoft.com/library/hh290136.aspx               225793
    msdn.microsoft.com/library/ee256749.aspx               143577
    msdn.microsoft.com/library/hh290138.aspx               237372
    msdn.microsoft.com/library/hh290140.aspx               128279
    msdn.microsoft.com/library/dd470362.aspx               157649
    msdn.microsoft.com/library/aa578028.aspx               204457
    msdn.microsoft.com/library/ms404677.aspx               176405
    msdn.microsoft.com/library/ff730837.aspx               143474
    
    Total bytes returned:  1834802
    
    Control returned to startButton_Click.
    

    Обратите внимание, что вывод результатов на экран занимает несколько секунд. В течение этого времени поток пользовательского интерфейса заблокирован, пока он ожидает загрузку запрошенных ресурсов. Соответственно, после нажатия кнопки Start окно нельзя перемещать, разворачивать, сворачивать или даже закрывать. Эти действия будут завершаться сбоем, пока не появятся результаты подсчета. Если веб-сайт не отвечает, вы не можете определить, на каком сайте произошел сбой. Трудно даже остановить ожидание и закрыть программу.

Преобразование GetURLContents в асинхронный метод

  1. Чтобы преобразовать синхронное решение в асинхронное решение, лучше всего начать, так как вызовы HttpWebRequest.GetResponse метода и Stream.CopyTo метода находятся там, где приложение обращается GetURLContents к Интернету. Платформа .NET Framework упрощает преобразование путем предоставления асинхронных версий этих методов.

    Дополнительные сведения о методах, которые используются в GetURLContents, см. в разделе WebRequest.

    Примечание.

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

    Измените метод, который вызывается в третьей строке GetURLContents из GetResponse, асинхронным методом GetResponseAsync, основанным на задачах.

    Using response As WebResponse = webReq.GetResponseAsync()
    
  2. GetResponseAsync возвращает значение типа Task<TResult>. В этом случае переменная, возвращаемая задачей, TResult, имеет тип WebResponse. Задача является обещанием создать фактический объект WebResponse после загрузки запрошенных данных и выполнения задачи до завершения.

    Чтобы получить значение из задачи, примените WebResponseоператор Await к вызову GetResponseAsync, как показано в следующем коде.

    Using response As WebResponse = Await webReq.GetResponseAsync()
    

    Оператор Await приостанавливает выполнение текущего метода GetURLContents, пока не будет завершена ожидаемая задача. На это время управление возвращается вызывающему объекту текущего метода. В этом примере текущим методом является GetURLContents, а вызывающим объектом — SumPageSizes. После завершения задачи создается обещанный объект WebResponse в качестве значения ожидаемой задачи, который присваивается переменной response.

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

    Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync()
    Using response As WebResponse = Await responseTask
    

    Вызов webReq.GetResponseAsync возвращает Task(Of WebResponse) или Task<WebResponse>. Await Затем оператор применяется к задаче для получения WebResponse значения.

    Если асинхронный метод выполняет какие-то действия, это не зависит от выполнения задачи, метод может продолжать свои действия между выполнениями этих двух операторов: после вызова асинхронного метода и перед применением оператора await. Примеры см. в статье "Практическое руководство. Создание нескольких веб-запросов параллельно с помощью асинхронного и ожидаемого(Visual Basic) и практическое руководство. Расширение асинхронного пошагового руководства с помощью task.WhenAll (Visual Basic)".

  3. Из-за добавления оператора Await в предыдущем шаге возникает ошибка компилятора. Оператор можно использовать только в методах, помеченных модификатором Async . Пропустите ошибку, повторяя действия по замене вызова CopyTo вызовом метода CopyToAsync.

    • Измените имя метода, вызывающего CopyToAsync.

    • Метод CopyTo или CopyToAsync копирует байты в свой аргумент content и не возвращает осмысленное значение. В синхронной версии вызов метода CopyTo — это просто оператор, который не возвращает значение. Асинхронная версия — CopyToAsync — возвращает Task. Задача работает как Task(void) и позволяет ожидать метод. Примените Await или await к вызову CopyToAsync, как показано в следующем примере кода.

      Await responseStream.CopyToAsync(content)
      

      Предыдущий оператор сокращает две следующие строки кода.

      ' CopyToAsync returns a Task, not a Task<T>.
      Dim copyTask As Task = responseStream.CopyToAsync(content)
      
      ' When copyTask is completed, content contains a copy of
      ' responseStream.
      Await copyTask
      
  4. Все, что остается сделать в GetURLContents, — это изменить подпись метода. Оператор можно использовать Await только в методах, помеченных модификатором Async . Добавьте модификатор, чтобы пометить метод как асинхронный, как показано в приведенном ниже примере кода.

    Private Async Function GetURLContents(url As String) As Byte()
    
  5. Возвращаемый тип асинхронного метода может быть Taskтолько . Task<TResult> В Visual Basic метод должен являться функцией Function, возвращающей Task или Task(Of T), либо он должен быть Sub. Как правило, Sub метод используется только в асинхронном обработчике событий, где Sub это необходимо. В других случаях используется Task(T) , если завершенный метод имеет оператор Return , возвращающий значение типа T, и используется Task , если завершенный метод не возвращает понятное значение.

    Дополнительные сведения см. в статье Async Return Types (Visual Basic).

    Метод GetURLContents имеет оператор return, который возвращает массив байтов. Таким образом, тип возвращаемого значения асинхронной версии — Task(T), где T — массив байтов. Внесите следующие изменения в подпись метода.

    • Измените тип возвращаемого значения на Task(Of Byte()).

    • По соглашению об именовании асинхронные методы имеют имена, заканчивающиеся на Async, поэтому переименуйте метод в GetURLContentsAsync.

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

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
    

    После внесения этих изменений преобразование GetURLContents в асинхронный метод завершено.

Преобразование SumPageSizes в асинхронный метод

  1. Повторите шаги из предыдущей процедуры для SumPageSizes. Во-первых, преобразуйте вызов метода GetURLContents в вызов асинхронного метода.

    • Измените имя вызываемого метода с GetURLContents на GetURLContentsAsync, если это еще не сделано.

    • Примените оператор Await к задаче, возвращаемой методом GetURLContentsAsync, для получения значения байтового массива.

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

    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    

    Предыдущее назначение сокращает две следующие строки кода.

    ' GetURLContentsAsync returns a task. At completion, the task
    ' produces a byte array.
    Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
    Dim urlContents As Byte() = Await getContentsTask
    
  2. Внесите следующие изменения в подпись метода.

    • Пометьте метод модификатором Async.

    • Добавьте в имя метода Async.

    • На этот раз нет возвращаемой переменной задачи T, так как SumPageSizesAsync не возвращает значение для T. (Метод Return не имеет инструкции.) Однако метод должен возвращать ожидаемый Task объект. Поэтому измените тип метода на SubFunction. Тип значения, возвращаемого функцией, — Task.

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

    Private Async Function SumPageSizesAsync() As Task
    

    Преобразование SumPageSizes в SumPageSizesAsync завершено.

Преобразование startButton_Click в асинхронный метод

  1. В обработчике событий измените имя вызываемого метода с SumPageSizes на SumPageSizesAsync, если это еще не сделано.

  2. Поскольку SumPageSizesAsync является асинхронным методом, измените код в обработчике событий для ожидания результата.

    Вызов SumPageSizesAsync отражает вызов CopyToAsync в GetURLContentsAsync. Вызов возвращает Task, а не Task(T).

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

    ' One-step async call.
    Await SumPageSizesAsync()
    
    ' Two-step async call.
    Dim sumTask As Task = SumPageSizesAsync()
    Await sumTask
    
  3. Чтобы предотвратить случайное повторное введение операции, добавьте следующий оператор в верхней части startButton_Click, чтобы отключить кнопку Start.

    ' Disable the button until the operation is complete.
    startButton.IsEnabled = False
    

    Можно снова включить кнопку в конце обработчика событий.

    ' Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = True
    

    Дополнительные сведения о повторном входе см. в разделе "Обработка повторного входа" в асинхронных приложениях (Visual Basic).

  4. Наконец, добавьте модификатор Async в объявление, чтобы обработчик событий мог ожидать SumPagSizesAsync.

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
    

    Как правило, имена обработчиков событий не изменяются. Возвращаемый тип не изменяется, Task так как обработчики событий должны быть Sub процедурами в Visual Basic.

    Преобразование проекта из синхронного в асинхронный завершено.

Тестирование асинхронного решения

  1. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start .

  2. Появившиеся результаты должны напоминать результаты синхронного решения. Однако имеются следующие различия.

    • Все результаты не отображаются одновременно после завершения обработки. Например, обе программы содержат строку в startButton_Click, которая очищает текстовое поле. Ее цель состоит в том, чтобы очистить текстовое поле между запусками при нажатии кнопки Start во второй раз после появления одного набора результатов. В синхронной версии текстовое поле очищается перед отображением данных во второй раз, после завершения загрузки и высвобождения потока пользовательского интерфейса для выполнения других операций. В асинхронной версии текстовое поле очищается сразу же после нажатия кнопки Start.

    • И что самое главное, поток пользовательского интерфейса не блокируется во время загрузки. Можно перемещать окно или изменять его размер во время загрузки, подсчета и отображения веб-ресурсов. Если один из веб-сайтов работает медленно или не отвечает, можно отменить операцию, нажав кнопку Закрыть (красный крестик в правом верхнем углу окна).

Замените метод GetURLContentsAsync методом платформа .NET Framework

  1. Платформа .NET Framework предоставляет множество асинхронных методов, которые можно использовать. Один из них, метод, делает только то, HttpClient.GetByteArrayAsync(String) что вам нужно для этого пошагового руководства. Его можно использовать вместо метода GetURLContentsAsync, созданного в предыдущей процедуре.

    Первым шагом является создание HttpClient объекта в методе SumPageSizesAsync . Добавьте следующее объявление в начале метода.

    ' Declare an HttpClient object and increase the buffer size. The
    ' default buffer size is 65,536.
    Dim client As HttpClient =
        New HttpClient() With {.MaxResponseContentBufferSize = 1000000}
    
  2. В SumPageSizesAsync, замените вызов метода GetURLContentsAsync вызовом метода HttpClient.

    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
  3. Удалите или прокомментируйте метод GetURLContentsAsync, который вы написали.

  4. Нажмите клавишу F5, чтобы запустить программу, а затем нажмите кнопку Start .

    Поведение этой версии проекта должно соответствовать поведению, которое описывается в процедуре "Тестирование асинхронного решения"; при этом с вашей стороны требуется даже меньше усилий.

Пример

Ниже приведен полный пример преобразованного асинхронного решения, использующего асинхронный GetURLContentsAsync метод. Обратите внимание, что он очень напоминает исходное синхронное решение.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        ' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Make a list of web addresses.
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            Dim urlContents As Byte() = Await GetURLContentsAsync(url)

            ' The previous line abbreviates the following two assignment statements.

            '//<snippet21>
            ' GetURLContentsAsync returns a task. At completion, the task
            ' produces a byte array.
            'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url)
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content.
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL.
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for
        ' the response.
        Using response As WebResponse = Await webReq.GetResponseAsync()

            ' The previous statement abbreviates the following two statements.

            'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync()
            'Using response As WebResponse = Await responseTask

            ' Get the data stream that is associated with the specified URL.
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.
                Await responseStream.CopyToAsync(content)

                ' The previous statement abbreviates the following two statements.

                ' CopyToAsync returns a Task, not a Task<T>.
                'Dim copyTask As Task = responseStream.CopyToAsync(content)

                ' When copyTask is completed, content contains a copy of
                ' responseStream.
                'Await copyTask
            End Using
        End Using

        ' Return the result as a byte array.
        Return content.ToArray()
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format
        ' is designed to be used with a monospaced font, such as
        ' Lucida Console or Global Monospace.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

Следующий код содержит полный пример решения, использующего метод HttpClient, GetByteArrayAsync.

' Add the following Imports statements, and add a reference for System.Net.Http.
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False

        ' One-step async call.
        Await SumPageSizesAsync()

        ' Two-step async call.
        'Dim sumTask As Task = SumPageSizesAsync()
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click."

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True
    End Sub

    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The
        ' default buffer size is 65,536.
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

        ' Make a list of web addresses.
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            ' GetByteArrayAsync returns a task. At completion, the task
            ' produces a byte array.
            Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)

            ' The following two lines can replace the previous assignment statement.
            'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url)
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function

    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/library/hh290136.aspx",
                "https://msdn.microsoft.com/library/ee256749.aspx",
                "https://msdn.microsoft.com/library/hh290138.aspx",
                "https://msdn.microsoft.com/library/hh290140.aspx",
                "https://msdn.microsoft.com/library/dd470362.aspx",
                "https://msdn.microsoft.com/library/aa578028.aspx",
                "https://msdn.microsoft.com/library/ms404677.aspx",
                "https://msdn.microsoft.com/library/ff730837.aspx"
            }
        Return urls
    End Function

    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format
        ' is designed to be used with a monospaced font, such as
        ' Lucida Console or Global Monospace.
        Dim bytes = content.Length
        ' Strip off the "https://".
        Dim displayURL = url.Replace("https://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub

End Class

См. также