我有一个从url获取HTML的方法,通过解析它来提取实体,并返回entites列表。这是示例代码:
public List<Entity> FetchEntities()
{
List<Entity> myList = new List<Entity>();
string url = "<myUrl>";
string response = String.Empty;
client = new WebClient();
client.DownloadStringCompleted += (sender, e) =>
{
response = e.Result;
// parse response
// extract content and generate entities
// <---- I am currently filling list here
};
client.DownloadStringAsync(new Uri(url));
return myList;
}
问题是当异步调用正在进行时,控制返回空myList
。我怎么能阻止这个。我的最终目标是返回已填写列表。
此方法也在一个单独的类库项目中,并从windows phone应用程序调用,我必须保持它只是这样。有没有办法做到这一点,或者我错过了什么?任何帮助将不胜感激。
答案 0 :(得分:4)
这就是异步编程非阻塞的重点。您可以将回调作为参数传递,并在其他地方处理结果,而不是尝试返回它。
如果您需要返回结果,可以使用this TPL library,我已经暂时使用它了一段时间。
public Task<string> GetWebResultAsync(string url)
{
var tcs = new TaskCompletionSource<string>();
var client = new WebClient();
DownloadStringCompletedEventHandler h = null;
h = (sender, args) =>
{
if (args.Cancelled)
{
tcs.SetCanceled();
}
else if (args.Error != null)
{
tcs.SetException(args.Error);
}
else
{
tcs.SetResult(args.Result);
}
client.DownloadStringCompleted -= h;
};
client.DownloadStringCompleted += h;
client.DownloadStringAsync(new Uri(url));
return tcs.Task;
}
}
调用它正是你在.net 4.0中使用TPL的原因
GetWebResultAsnyc(url).ContinueWith((t) =>
{
t.Result //this is the downloaded string
});
或:
var downloadTask = GetWebResultAsync(url);
downloadTask.Wait();
var result = downloadTask.Result; //this is the downloaded string
希望这会有所帮助:)
答案 1 :(得分:4)
你可以将回调传递给这样的方法,并在没有任务的情况下使其成为异步,所以你必须稍微更新方法用法。
public void FetchEntities(
Action<List<Entity>> resultCallback,
Action<string> errorCallback)
{
List<Entity> myList = new List<Entity>();
string url = "<myUrl>";
string response = String.Empty;
client = new WebClient();
client.DownloadStringCompleted += (sender, e) =>
{
response = e.Result;
// parse response
// extract content and generate entities
// <---- I am currently filling list here
if (response == null)
{
if (errorCallback != null)
errorCallback("Ooops, something bad happened");
}
else
{
if (callback != null)
callback(myList);
}
};
client.DownloadStringAsync(new Uri(url));
}
另一种选择是强制它是同步的。像那样
public List<Entity> FetchEntities()
{
List<Entity> myList = new List<Entity>();
string url = "<myUrl>";
string response = String.Empty;
client = new WebClient();
AutoResetEvent waitHandle = new AutoResetEvent(false);
client.DownloadStringCompleted += (sender, e) =>
{
response = e.Result;
// parse response
// extract content and generate entities
// <---- I am currently filling list here
waitHandle.Set();
};
client.DownloadStringAsync(new Uri(url));
waitHandle.WaitOne();
return myList;
}