【发布时间】:2013-04-07 06:07:49
【问题描述】:
以下是 C# async 方法的代码,该方法还具有两个 WebClient 控件事件的回调处理程序:DownloadProgressChanged 和 OpenReadCompleted。当我运行代码时,最初它会流向“await DownloadStringTaskAsync()”调用并退出。然后我看到 DownloadProgressChanged 的匿名事件处理程序代码触发,这就是我遇到问题的地方。然后代码流向“return strRet”语句,因此该方法的返回值是分配给该方法顶部的 strRet 的初始化值“(none)”,而不是 OpenReadCompleted 匿名回调中分配给 strRet 的网页内容。
所以我需要等待 OpenReadCompleted 回调在控制流到 return 语句之前执行,但我不确定如何正确执行此操作。如何更正代码,使其在执行 OpenReadCompleted 回调之前不会到达“return strRet”语句?
/// <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
=================================
更新:根据我收到的答案,我已将代码修改为以下内容:
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
【问题讨论】:
-
我想在继续之前验证这一点 - 你能提供一个简短但完整的程序来演示这个问题吗?
-
@JonSkeet - 请参阅 outcoldman 对我的回答,因为它包含了我的问题的真正原因。
标签: c# .net asynchronous task async-await