Ответ 1
Вы правы в TaskCompletionSource, это ключ к преобразованию EAP (асинхронный шаблон на основе событий) в TPL Task.
Это описано здесь: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/tpl-and-traditional-async-programming#exposing-complex-eap-operations-as-tasks
Вот упрощенный код:
public static class Extensions
{
public static Task<XDocument> GetRssDownloadTask(
this WebClient client, Uri rssFeedUri)
{
// task completion source is an object, which has some state.
// it gives out the task, which completes, when state turns "completed"
// or else it could be canceled or throw an exception
var tcs = new TaskCompletionSource<XDocument>();
// now we subscribe to completed event. depending on event result
// we set TaskCompletionSource state completed, canceled, or error
client.DownloadStringCompleted += (sender, e) =>
{
if(e.Cancelled)
{
tcs.SetCanceled();
}
else if(null != e.Error)
{
tcs.SetException(e.Error);
}
else
{
tcs.SetResult(XDocument.Parse(e.Result));
}
};
// now we start asyncronous operation
client.DownloadStringAsync(rssFeedUri);
// and return the underlying task immediately
return tcs.Task;
}
}
Теперь все, что вам нужно сделать, чтобы сделать цепочку этих операций, - это просто установить ваши продолжения (что на данный момент не очень удобно, а С# 5 ждет, и async сильно поможет с ним)
Итак, этот код можно использовать следующим образом:
public static void Main()
{
var client = new WebClient();
client.GetRssDownloadTask(
new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"))
.ContinueWith( t => {
ShowXmlInMyUI(t.Result); // show first result somewhere
// start a new task here if you want a chain sequence
});
// or start it here if you want to get some rss feeds simultaneously
// if we had await now, we would add
// async keyword to Main method defenition and then
XDocument feedEric = await client.GetRssDownloadTask(
new Uri("http://blogs.msdn.com/b/ericlippert/rss.aspx"));
XDocument feedJon = await client.GetRssDownloadTask(
new Uri("http://feeds.feedburner.com/JonSkeetCodingBlog?format=xml"));
// it chaining - one task starts executing after
// another, but it is still asynchronous
}