Condividi tramite


Procedura dettagliata: accesso al Web tramite Async e Await (C# e Visual Basic)

È possibile scrivere programmi asincroni in modo più semplice e intuitivo utilizzando le funzionalità introdotte in Visual Studio 2012. È possibile scrivere codice asincrono simile al codice sincrono,lasciano al compilatore la gestione delle difficili funzioni di callback e delle continuazioni che il codice asincrono generalmente comporta.

Per ulteriori informazioni sulla funzionalità asincrona, vedere Programmazione asincrona con Async e Await (C# e Visual Basic).

Questa guida inizia con un'applicazione sincrona Windows Presentation Foundation (WPF) che somma il numero di byte in un elenco di siti web. La guida quindi converte l'applicazione in una soluzione asincrona utilizzando le nuove funzionalità.

Se non si desidera sviluppare applicazioni autonomamente, è possibile scaricare "esempio di Async: Accesso alla procedura dettagliata Web (c e Visual Basic)" da Esempi di codice dello sviluppatore.

In questa guida, completerete le seguenti attività:

  • Create a WPF application.

  • Design a simple WPF MainWindow window.

  • Add a reference.

  • Add Imports statements or using directives.

  • Create a synchronous solution.

  • Test the synchronous solution.

  • Convert GetURLContents to an asynchronous method.

  • Convert SumPageSizes to an asynchronous method.

  • Convert startButton_Click to an asynchronous method.

  • Test the asynchronous solution.

  • Replace GetURLContentsAsync with a .NET Framework method.

  • Complete Code Examples from the Walkthrough

Prerequisiti

Visual Studio 2012 deve essere installato nel vostro computer. Per ulteriori informazioni, vedere il sito Web Microsoft.

Per creare un'applicazione WPF

  1. Avviare Visual Studio.

  2. Nella barra del menu, scegliere File, Nuovo, Progetto.

    Verrà visualizzata la finestra di dialogo Nuovo progetto.

  3. Nel riquadro Modelli installati, scegliere Visual Basic o **Visual C#**quindi scegliere Applicazione WPF dall'elenco di tipi di progetto.

  4. Nella casella di testo Nome, immettere AsyncExampleWPFquindi scegliere il pulsante OK.

    Il nuovo progetto verrà visualizzato in Esplora soluzioni.

Per progettare un semplice Finestra WPF

  1. Nell'editor di codice di Visual Studio, scegliere la scheda MainWindow.xaml.

  2. Se la finestra Casella degli strumenti non è visibile, aprire il menu Vista , quindi scegliere Casella degli strumenti.

  3. Aggiungere un controllo Button e un controllo TextBox alla finestra MainWindow.

  4. Evidenziare il controllo TextBox e, nella finestra Proprietà, impostare i seguenti valori:

    • Impostare la proprietà Nome per resultsTextBox.

    • Impostare la proprietà Height a 250.

    • Impostare la proprietà Width a 500.

    • Nella scheda Testo, specificare un carattere monospaced, come Lucida Console o Global Monospace.

  5. Evidenziare il controllo Button e, nella finestra Proprietà, impostare i seguenti valori:

    • Imposta la proprietà Nome a startButton.

    • Modificare il valore della proprietà Contenuto da Button a Start.

  6. Posizionare la casella di testo e il pulsante in modo che entrambi vengano visualizzati nella finestra MainWindow.

    Per ulteriori informazioni sulla finestra di progettazione WPF XAML, vedere Creazione di un'interfaccia utente tramite XAML Designer.

Per aggiungere un riferimento

  1. In Esplora soluzioni, evidenziare il nome del vostro progetto.

  2. Nella barra dei menu, scegliere Progetto, Aggiungi riferimento.

    Verrà visualizzata la finestra di dialogo Gestione riferimenti.

  3. Nella parte superiore della finestra di dialogo, verificare che il vostro progetto si rivolga a .NET Framework 4.5.

  4. Nell'area Assembly, scegliere Framework se non è già stato selezionato.

  5. Nell'elenco dei nomi, selezionare la casella di controllo System.Net.Http.

  6. Scegliere OK per chiudere la finestra di dialogo.

Per aggiungere le istruzioni Imports o le direttive using necessarie

  1. In Esplora soluzioni, aprire il menu di scelta rapida per MainWindow.xaml.vb o MainWindow.xaml.cs e scegliere Visualizza Codice.

  2. Aggiungere le seguenti istruzioni Imports (Visual Basic) o direttive using (C#) all'inizio del file di codice, se non sono già presenti.

    Imports System.Net.Http
    Imports System.Net
    Imports System.IO
    
    using System.Net.Http;
    using System.Net;
    using System.IO;
    

Creare una applicazione sincrona

  1. Nella finestra di progettazione, fare doppio clic sul pulsante Start per creare il gestore di eventi startButton_Click nel file MainWindow.Xaml.vb o MainWindow.xaml.cs. In alternativa, evidenziare il pulsante Avvia, selezionare l'icona Gestore eventi per gli elementi selezionati nella finestra Proprietà , quindi immettere startButton_Click nella casella di testo Clic.

  2. Nel file MainWindow.xaml.vb o MainWindow.xaml.cs copiare il seguente codice nel corpo di startButton_Click.

    resultsTextBox.Clear()
    SumPageSizes()
    resultsTextBox.Text &= vbCrLf & "Control returned to startButton_Click."
    
    resultsTextBox.Clear();
    SumPageSizes();
    resultsTextBox.Text += "\r\nControl returned to startButton_Click.";
    

    Il codice chiama il metodo che controlla l'applicazione SumPageSizes; viene visualizzato un messaggio quando il controllo ritorna al startButton_Click.

  3. Il codice per la soluzione sincrona contiene i quattro metodi seguenti:

    • SumPageSizes, che ottiene un elenco di URL di pagine Web da SetUpURLList quindi chiama GetURLContents e DisplayResults per elaborare ogni URL.

    • SetUpURLList, che crea e restituisce un elenco di indirizzi web.

    • GetURLContents, che scarica il contenuto di ogni sito web e restituisce il contenuto come array di byte.

    • DisplayResults, che visualizza il numero di byte nell'array di byte per ogni URL.

    Copiare i seguenti quattro metodi e quindi incollarli nel gestore eventi startButton_Click del file MainWindow.xaml.vb o MainWindow.xaml.cs.

    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/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/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 "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub
    
    private void SumPageSizes()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList(); 
    
        var total = 0;
        foreach (var url in urlList)
        {
            // GetURLContents returns the contents of url as a byte array. 
            byte[] urlContents = GetURLContents(url);
    
            DisplayResults(url, urlContents);
    
            // Update the total.
            total += urlContents.Length;
        }
    
        // Display the total count for all of the web addresses.
        resultsTextBox.Text += 
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
    
    private List<string> SetUpURLList()
    {
        var urls = new List<string> 
        { 
            "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
            "https://msdn.microsoft.com",
            "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
            "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
            "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
            "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
            "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
            "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
        };
        return urls;
    }
    
    
    private byte[] GetURLContents(string url)
    {
        // The downloaded resource ends up in the variable named content. 
        var content = new MemoryStream();
    
        // Initialize an HttpWebRequest for the current URL. 
        var webReq = (HttpWebRequest)WebRequest.Create(url);
    
        // 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 (WebResponse response = webReq.GetResponse())
        {
            // Get the data stream that is associated with the specified URL. 
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content);
            }
        }
    
        // Return the result as a byte array. 
        return content.ToArray();
    }
    
    
    private void DisplayResults(string url, byte[] content)
    {
        // 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. 
        var bytes = content.Length;
        // Strip off the "http://".
        var displayURL = url.Replace("http://", "");
        resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
    }
    

Testare la soluzione sincrona

  • Premere il tasto F5 per eseguire il programma, quindi scegliere il pulsante Avvia.

    Dovrebbe risultare un output simile al seguente.

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

    Si noti che saranno necessari alcuni secondi per visualizzare i conteggi. Durante questo intervallo, il thread UI è bloccato mentre attende le risorse richieste da scaricare. Di conseguenza, non è possibile spostare, ingrandire, ridurre al minimo, o addirittura chiudere la finestra dopo avere scelto il pulsante Avvia. Questi sforzi hanno esito negativo fino a quando il conteggio di byte inizia ad essere visualizzato. Se un sito web non risponde, non vi è nessuna indicazione su quale sito sia. È anche difficile fermare l'attesa del programma e chiuderlo.

    Confrontare questo comportamento per la Esempio con una soluzione asincrona.

Convertire un GetURLContents in un metodo asincrono

  1. Per convertire la soluzione sincrona ad asincrona, il miglior posto per cominciare consiste in GetURLContents poiché le chiamate al metodo GetResponse HttpWebRequest e il metodo CopyTo Stream sono dove l'applicazione accede al web. Il .NET Framework semplifica la conversione fornendo versioni asincrone di entrambi i metodi.

    Per ulteriori informazioni sui metodi usati in GetURLContents, vedere WebRequest.

    Nota

    Seguendo i passi in questa guida, appariranno diversi errori del compilatore.È possibile ignorarli e continuare con la guida.

    Modificare il metodo chiamato nella terza riga di GetURLContents da GetResponse con il metodo asincrono basato su attività GetResponseAsync.

    Using response As WebResponse = webReq.GetResponseAsync()
    
    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync restituisce un Task. In questo caso, la variabile restituita dall' attività, TResult, è di tipo WebResponse. L'attività è una promessa di produrre effettivamente un oggetto WebResponse dopo che i dati richiesti sono stati scaricati e l'attività è stata completata.

    Per recuperare il valore WebResponse dall'attività, applicare un operatore Await (Visual Basic) o await (C#) alla chiamata a GetResponseAsync, come illustrato nel seguente codice.

    Using response As WebResponse = Await webReq.GetResponseAsync()
    
    using (WebResponse response = await webReq.GetResponseAsync())
    

    L'operatore di attesa sospende l'esecuzione del metodo corrente, GetURLContents, finché l'attività in attesa termina. Nel frattempo, il controllo ritorna al chiamante del metodo corrente. In questo esempio, il metodo corrente è GetURLContents, mentre il chiamante è SumPageSizes. Quando l'attività viene completata, l'oggetto promesso WebResponse viene generato come valore dell'attività di attesa e assegnato alla variabile response.

    L'istruzione precedente può essere separata nelle due istruzioni seguenti per chiarire cosa accade.

    'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
    'Using response As WebResponse = Await responseTask
    
    //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
    //using (WebResponse response = await responseTask)
    

    La chiamata a webReq.GetResponseAsync restituisce un Task(Of WebResponse) o un Task<WebResponse>. Quindi un operatore di attesa viene applicato all'attività recuperare il valore WebResponse.

    Se il vostro metodo async deve eseguire del lavoro che non dipende dal completamento dell'attività, il metodo può continuare con tale lavoro tra queste due istruzioni, dopo la chiamata al metodo async e prima che l'operatore di attesa venga applicato. Per i relativi esempi, vedere Procedura: effettuare più richieste Web in parallelo tramite Async e Await (C# e Visual Basic) e Procedura: estendere la procedura dettagliata asincrona tramite Task.WhenAll (C# e Visual Basic).

  3. Poiché è stato aggiunto l'operatore await o Await nel passaggio precedente, si verifica un errore del compilatore. L'operatore può essere utilizzato solo nei metodi contrassegnati con il modificatore Async (Visual Basic) o async (C#). Ignorare l'errore mentre ripetete la procedura di conversione per sostituire la chiamata a CopyTo con una chiamata a CopyToAsync.

    • Cambiate il nome del metodo che viene chiamato da CopyToAsync.

    • Il metodo CopyToAsync o CopyTo copia i byte dal proprio argomento, contente non restituisce alcun valore significativo. Nella versione sincrona, la chiamata a CopyTo è una semplice istruzione che non restituisce un valore. La versione asincrona, CopyToAsync, restituisce un Task. Le funzioni di attività come "Task(void)" and consentono di mettere il metodo in attesa. Applicare Await o await alla chiamata a CopyToAsync, come illustrato nel seguente codice.

      Await responseStream.CopyToAsync(content)
      
      await responseStream.CopyToAsync(content);
      

      L'istruzione precedente riduce di due righe il seguente codice.

      ' 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
      
      // CopyToAsync returns a Task, not a Task<T>. 
      //Task copyTask = responseStream.CopyToAsync(content); 
      
      // When copyTask is completed, content contains a copy of 
      // responseStream. 
      //await copyTask;
      
  4. Tutto ciò che rimane fare in GetURLContents è regolare la firma del metodo. L'operatore Await o await può essere utilizzato solo nei metodi contrassegnati con il modificatore Async (Visual Basic) o async (C#). Aggiungere il modificatore per contrassegnare il metodo come un metodo async, come mostrato nel seguente codice.

    Private Async Function GetURLContents(url As String) As Byte()
    
    private async byte[] GetURLContents(string url)
    
  5. Il tipo restituito di un metodo async può essere solo Task, Task, o void in C#. In Visual Basic, il metodo deve essere un Function che restituisce un Task o Task(Of T), o il metodo deve essere un Sub. In genere, un metodo Sub (Visual Basic) o un tipo restituito void (C#) viene utilizzato solo in un gestore eventi async, in cui Sub o void è obbligatorio. Negli altri casi, utilizzare Task(T) se il metodo ha completato un'istruzione Return o return che restituisce un valore di tipo T, e utilizzare Task se il metodo completato non restituisce alcun valore significativo. È possibile considerare il tipo restituito Task come "Task(void)".

    Per ulteriori informazioni, vedere Tipi restituiti asincroni (C# e Visual Basic).

    Il Metodo GetURLContents possiede un'istruzione return che restituisce un array di byte. Pertanto, il tipo restituito della versione async è Task(T), dove T è un array di byte. Apportare le seguenti modifiche nella firma del metodo :

    • Modificare il tipo restituito in Task(Of Byte()) (Visual Basic) o in Task<byte[]> (C#).

    • Per convenzione, i metodi asincroni dispongono di nomi che terminano in "Async", quindi rinominare il metodo GetURLContentsAsync.

    Il codice seguente illustra questi cambiamenti.

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
    
    private async Task<byte[]> GetURLContentsAsync(string url)
    

    Con queste poche modifiche, la conversione di GetURLContents in un metodo asincrono è completa.

Convertire SumPageSizes in un metodo asincrono

  1. Ripetere i passaggi della procedura precedente per SumPageSizes. Per prima cosa, modificare la chiamata a GetURLContents in una chiamata asincrona.

    • Se non è stato fatto precedentemente, modificare il nome del metodo chiamato da GetURLContents a GetURLContentsAsync.

    • Applicare Await o await all'attività che GetURLContentsAsync restituisce per ottenere il valore di un array di byte.

    Il codice seguente illustra questi cambiamenti.

    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    byte[] urlContents = await GetURLContentsAsync(url);
    

    L'istruzione precedente riduce di due righe il seguente codice.

    ' 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
    
    // GetURLContentsAsync returns a Task<T>. At completion, the task 
    // produces a byte array. 
    //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
    //byte[] urlContents = await getContentsTask;
    
  2. Apportare le seguenti modifiche nella firma del metodo :

    • Contrassegnare il metodo con il modificatore async o Async.

    • Aggiungere "Async" al nome del metodo.

    • Non vi è alcuna variabile di attività restituita T questa volta, perché SumPageSizesAsync non restituisce un valore per T. (Il metodo non dispone dell'istruzione return o Return ). Tuttavia, il metodo deve restituire un Task per essere awaitable. Di conseguenza, effettuare una delle seguenti modifiche:

      • In Visual Basic, modificare il tipo di metodo da Sub a Function. Il tipo restituito dalla funzione è Task.

      • In C#, modificare il tipo restituito del metodo da void a Task.

    Il codice seguente illustra questi cambiamenti.

    Private Async Function SumPageSizesAsync() As Task
    
    private async Task SumPageSizesAsync()
    

    La conversione da SumPageSizes a SumPageSizesAsync è completa.

Convertire startButton Clic in un metodo asincrono

  1. Nel gestore eventi, modificare il nome del metodo chiamato da SumPageSizes a SumPageSizesAsync, se necessario.

  2. Poiché SumPageSizesAsync è un metodo async, modificare il codice nel gestore eventi in attesa del risultato.

    La chiamata a SumPageSizesAsync rispecchia la chiamata a CopyToAsync in GetURLContentsAsync. La chiamata restituisce un Task, non un Task(T).

    Come nelle precedenti procedure, è possibile convertire la chiamata utilizzando una o due istruzioni. Il codice seguente illustra questi cambiamenti.

    '' One-step async call.
    Await SumPageSizesAsync()
    
    ' Two-step async call. 
    'Dim sumTask As Task = SumPageSizesAsync() 
    'Await sumTask
    
    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call. 
    //Task sumTask = SumPageSizesAsync(); 
    //await sumTask;
    
  3. Per impedire accidentalmente registrare nuovamente l'operazione, aggiungere la seguente istruzione all'inizio di startButton_Click per disabilitare il pulsante Avvio.

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

    È possibile riabilitare il pulsante alla fine del gestore eventi.

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

    Per ulteriori informazioni sul motivo, vedere Gestione della reentrancy nelle applicazioni asincrone (C# e Visual Basic).

  4. Infine, aggiungere il modificatore async o Async alla dichiarazione in modo che il gestore di eventi possa attendere SumPagSizesAsync.

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
    
    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    In genere, i nomi dei gestori eventi non vengono modificati. Il tipo restituito non viene modificato in Task poiché i gestori eventi devono restituire void in C# o essere routine Sub in Visual Basic. Pertanto, il tipo restituito da Task.

    La conversione del progetto dall'elaborazione sincrona a quella asincrona è completa.

Testare la soluzione asincrona

  1. Premere il tasto F5 per eseguire il programma, quindi scegliere il pulsante Avvia.

  2. Dovrebbe venir visualizzato un output che assomiglia all'output della soluzione sincrona. Esistono tuttavia le differenze seguenti.

    • Tutti i risultati non si verificano contemporaneamente dopo che l'elaborazione è completa. Ad esempio, entrambi i programmi contengono una riga in startButton_Click che consente di pulire la casella di testo. L'obiettivo è quello di resettare la casella di testo tra le varie esecuzioni, in questo modo se si sceglie il pulsante Avvia una seconda volta, nessun gruppo di risultati sarà già presente. Nella versione sincrona, la casella di testo viene resettata appena prima che i risultati vengano visualizzati per la seconda volta, ovvero quando vengono completati i download e il thread dell'interfaccia utente è libero di eseguire altre operazioni. Nella versione asincrona, le casella la casella di testo viene resettata immediatamente dopo aver scelto il pulsante Avvia.

    • In particolare, il thread dell'interfaccia utente non è bloccato durante i download. È possibile spostare o ridimensionare la finestra mentre le risorse Web vengono scaricare, contate e visualizzate. Se uno dei siti Web ha una risposta lenta o non risponde, è possibile annullare l'operazione scegliendo il pulsante Chiudi (la x nel campo rosso nell'angolo superiore destro).

Sostituire il metodo GetURLContentsAsync con un metodo.NET Framework

  1. Il .NET Framework 4.5 fornisce molti metodi async che è possibile utilizzare. Uno di essi, il metodo HttpClient GetByteArrayAsync(String),, fa esattamente ciò che viene richiesto da questa guida. È possibile utilizzarlo all'interno del metodo GetURLContentsAsync creato in una procedura precedente.

    Il primo passaggio consiste nella creazione di un oggetto HttpClient nel metodo SumPageSizesAsync. Aggiungere la seguente dichiarazione all'inizio del metodo.

    ' 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}
    
    // Declare an HttpClient object and increase the buffer size. The 
    // default buffer size is 65,536.
    HttpClient client =
        new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
  2. Nel file SumPageSizesAsync, sostituire la chiamato al vostro metodo GetURLContentsAsync con una chiamata al metodo HttpClient .

    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
    byte[] urlContents = await client.GetByteArrayAsync(url);               
    
  3. Rimuovere o commentare il metodo GetURLContentsAsync da voi scritto.

  4. Premere il tasto F5 per eseguire il programma, quindi scegliere il pulsante Avvia.

    Il comportamento di questa versione del progetto deve corrispondere al comportamento che "Testare la routine della soluzione asincrona" descrive ma con meno lavoro richiesto da parte dell'utente.

Esempio

Il seguente codice contiene l'esempio completo della conversione della soluzione sincrono in una asincrona tramite il metodo asincrono GetURLContentsAsync da voi scritto. Si noti che è molto somigliante alla soluzione sincrona originale.

' 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. 

            ' 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/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/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 "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

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;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;

namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            resultsTextBox.Clear();

            // One-step async call.
            await SumPageSizesAsync();

            // Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                byte[] urlContents = await GetURLContentsAsync(url);

                // The previous line abbreviates the following two assignment statements. 

                // GetURLContentsAsync returns a Task<T>. At completion, the task 
                // produces a byte array. 
                //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.          
                total += urlContents.Length;
            }
            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private async Task<byte[]> GetURLContentsAsync(string url)
        {
            // The downloaded resource ends up in the variable named content. 
            var content = new MemoryStream();

            // Initialize an HttpWebRequest for the current URL. 
            var webReq = (HttpWebRequest)WebRequest.Create(url);

            // Send the request to the Internet resource and wait for 
            // the response.                 
            using (WebResponse response = await webReq.GetResponseAsync())

            // The previous statement abbreviates the following two statements. 

            //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
            //using (WebResponse response = await responseTask)
            {
                // Get the data stream that is associated with the specified url. 
                using (Stream responseStream = 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>. 
                    //Task copyTask = responseStream.CopyToAsync(content); 

                    // When copyTask is completed, content contains a copy of 
                    // responseStream. 
                    //await copyTask;
                }
            }
            // Return the result as a byte array. 
            return content.ToArray();
        }


        private void DisplayResults(string url, byte[] content)
        {
            // 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. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

Il codice seguente contiene l'esempio completo della soluzione che utilizza il metodo 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/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/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 "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

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;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;


namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            // One-step async call.
            await SumPageSizesAsync();

            //// Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Declare an HttpClient object and increase the buffer size. The 
            // default buffer size is 65,536.
            HttpClient client =
                new HttpClient() { MaxResponseContentBufferSize = 1000000 };

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                // GetByteArrayAsync returns a task. At completion, the task 
                // produces a byte array. 
                byte[] urlContents = await client.GetByteArrayAsync(url);               

                // The following two lines can replace the previous assignment statement. 
                //Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.
                total += urlContents.Length;
            }

            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private void DisplayResults(string url, byte[] content)
        {
            // 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. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

Vedere anche

Attività

Procedura: estendere la procedura dettagliata asincrona tramite Task.WhenAll (C# e Visual Basic)

Procedura: effettuare più richieste Web in parallelo tramite Async e Await (C# e Visual Basic)

Procedura dettagliata: utilizzo del debugger con metodi Async

Riferimenti

async (Riferimenti per C#)

await (Riferimenti per C#)

Opertore Await (Visual Basic)

Async (Visual Basic)

Concetti

Programmazione asincrona con Async e Await (C# e Visual Basic)

Tipi restituiti asincroni (C# e Visual Basic)

Utilizzo della funzionalità Async per l'accesso ai file (C# e Visual Basic)

Altre risorse

Esempio Async: Accesso alla guida web (C# e Visual Basic)

Programmazione Asincrona basata su Attività (TAP)

Guida rapida: utilizzare l'operatore di attesa per la programmazione asincrona