2013-04-06 28 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