2013-04-06 40 views
3

流C#異步方法下面是也有回調處理程序兩個WebClient的控件的事件的C#異步方法的代碼:DownloadProgressChangedOpenReadCompleted。當我運行代碼時,最初它流向「」,等待「DownloadStringTaskAsync()」調用並退出。然後我看到匿名事件處理程序代碼爲下載進度更改,這是我遇到問題的地方。代碼然後流向「return strRet」語句,因此該方法的返回值是在方法的頂部分配給strRet的初始化值「(none)」,而不是網頁的內容分配給strRetOpenReadCompleted匿名回調。也有匿名的回調處理程序不正確

所以我需要等待OpenReadCompleted回調在控制流到return語句之前執行,但我不知道如何正確執行此操作。我如何糾正代碼,使其未達到「return strRet」聲明,直到OpenReadCompleted回調已執行?

/// <summary> 
    /// This method downloads the contents of a URL to a string. Returns the URL contents 
    /// as a string if it succeeds, throws an Exception if not. 
    /// <param name="strUrl">The URL to download.</param> 
    /// <param name="progress">An IProgress object to report download progress to. May be NULL.</param> 
    /// <param name="cancelToken">A cancellation token. May be NULL.</param> 
    /// <param name="iNumSecondsToWait">The number of seconds to wait before cancelling the download. Default is 30 seconds</param> 
    /// </summary> 
    /// <remarks> 
    /// Use "await" with this method wrapped in Task.run() to manage the process asynchronously. 
    /// 
    /// NOTE: The DownloadProgressChanged() event is raised on the UI 
    /// thread so it is safe to do UI updates from the IProgress.Report() 
    /// method. 
    /// </remarks> 
    async public static Task<string> URLToString(string strUrl, IProgress<int> progress, CancellationToken cancelToken, int iNumSecondsToWait = 30) 
    { 
     // The string to be returned. 
     string strRet = "(none)"; 

     strUrl = strUrl.Trim(); 

     if (String.IsNullOrWhiteSpace(strUrl)) 
      throw new ArgumentException("(Misc::URLToString) The URL is empty."); 

     if (iNumSecondsToWait < 1) 
      throw new ArgumentException("(Misc::URLToString) The number of seconds to wait is less than 1."); 

     // Asynchronous download. Note, the Silverlight version of WebClient does *not* implement 
     // IDisposable. 
     WebClient wc = new WebClient(); 

     // Create a download progress changed handler so we can pass on progress 
     // reports to the caller if they provided a progress report object. 
     // This event is raised on the UI thread. 
     wc.DownloadProgressChanged += (s, e) => 
     { 
      // Do we have a progress report handler? 
      if (progress != null) 
       // Yes, call it. 
       progress.Report(e.ProgressPercentage); 

      // If we have a cancellation token and the operation was cancelled, then abort the download. 
      if (cancelToken != null) 
       cancelToken.ThrowIfCancellationRequested(); 

     }; // wc.DownloadProgressChanged += (s, e) => 

     // Use a Lambda expression for the "completed" handler 
     // that writes the downloaded contents as a string to a file. 
     wc.OpenReadCompleted += (s, e) => 
     { 
      // If we have a cancellation token and the operation was cancelled, then abort the download. 
      if (cancelToken != null) 
       cancelToken.ThrowIfCancellationRequested(); 

      // Return the downloaded file as a string. 
      strRet = e.Result.ToString(); 
     }; // wc.OpenReadCompleted += (s, e) => 

     // Now make the call to download the file and do an asynchronous wait for the result. 
     await wc.DownloadStringTaskAsync(new Uri(strUrl)); 

     // wc.DownloadStringAsync(new Uri(strUrl)); 

     return strRet; 
    } // async public static void URLToStr 

================================

UPDATE:基於答案我收到我已經修改了代碼如下:

async public static Task<string> URLToStringAsync(string strUrl, IProgress<int> progress, CancellationToken cancelToken, int iNumSecondsToWait = 30) 
    { 
     strUrl = strUrl.Trim(); 

     if (String.IsNullOrWhiteSpace(strUrl)) 
      throw new ArgumentException("(Misc::URLToStringAsync) The URL is empty."); 

     if (iNumSecondsToWait < 1) 
      throw new ArgumentException("(Misc::URLToStringAsync) The number of seconds to wait is less than 1."); 

     // Asynchronous download. Note, the Silverlight version of WebClient does *not* implement 
     // IDisposable. 
     WebClient wc = new WebClient(); 

     // Create a download progress changed handler so we can pass on progress 
     // reports to the caller if they provided a progress report object. 
     // This event is raised on the UI thread. 
     wc.DownloadProgressChanged += (s, e) => 
     { 
      // Do we have a progress report handler? 
      if (progress != null) 
       // Yes, call it. 
       progress.Report(e.ProgressPercentage); 

      // If we have a cancellation token and the operation was cancelled, then abort the download. 
      if (safeCancellationCheck(cancelToken)) 
       wc.CancelAsync(); 
     }; // wc.DownloadProgressChanged += (s, e) => 

     // Now make the call to download the file and do an asynchronous wait for the result. 
     return await wc.DownloadStringTaskAsync(new Uri(strUrl)); 
    } // async public static void URLToStringAsync 
+2

我想在進一步驗證之前進行驗證 - 您能否提供一個簡短但完整的程序來演示問題? – 2013-04-06 19:04:55

+0

@JonSkeet - 看到outcoldman對我的回答,因爲它包含了我的問題的真正原因。 – 2013-04-06 21:21:01

回答

3

我發現了幾個問題:

一)從MSDN文檔,它看起來像DownloadStringTaskAsync不火DownloadProgressChanged

b)OpenReadCompleted僅當您使用OpenReadAsync創建請求時纔會觸發事件。它不會被解僱DownloadStringTaskAsync。

c)你可以使用DownloadStringCompleted事件得到DownloadStringTaskAsync的結果,但爲什麼,如果你使用的是異步/等待你可以這樣做:

strRet = await wc.DownloadStringTaskAsync(new Uri(strUrl)); 
+0

謝謝。就是這樣,我很高興,否則我對異步/等待的理解就會崩潰。關於等待DownloadStringTaskAsync的提示也是如此。 – 2013-04-06 21:18:05

2

你混合幾種不同的異步的API。 DownloadProgressChangedOpenReadCompleted都是EAP events,而DownloadStringTaskAsyncTAP method

我建議您不斷使用EAP API或TAP API。更好的是,從WebClient轉換爲HttpClient

順便說一句,你可能不想從事件處理程序調用ThrowIfCancellationRequested。相反,請將您的CancellationToken連接到WebClient.CancelAsync

+0

查看outcoldman對我的回答,因爲它包含我的問題的原因。雖然你對不混合異步模式做出了很好的評價,但也贊同了你的觀點。感謝關於HttpClient的提示和WebClient.CancelAsync的提示。 – 2013-04-06 21:20:35

+0

更新:我不認爲Windows Phone 7有HttpClient。 System.Net沒有它,我檢查了我爲WP7安裝的Micrsoft.Bcl.Async軟件包,所以我在那裏也找不到它。猜猜我與WebClient卡住了,但至少我知道它有下載進度事件回調。 – 2013-04-06 21:50:57