我有一个列表,我需要加载产品名称,如:
List<string> prodNames= new List<string>();
我的系统上不存在产品数据。所以,我必须先下载它们。
将产品数据逐个下载到本地文件系统。下载产品会触发我处理的事件处理程序,处理程序会提供我需要添加到列表中的产品名称。
我知道有多少产品可以随时使用,比如5。
我想知道如何知道所有5个产品名称何时添加到我的列表中,以便我可以继续显示它们。这是伪代码
List ids = // list of product ids
List<string> prodNames= new List<string>(); //list to fill with prod names once they are downloaded
this.EventHandler += OnDownloadedProduct; //event fired once each product is downloaded
void GetProducts()
{
foreach (var id in ids)
{
// NOTE that I cannot modify neither downloader object nor GetProduct()
// method. These are not my code but come from a component for which
// I have no code access.
downloader.GetProduct(id); //fires OnDownloadedProduct once product is downloaded
}
}
//this event is fired once each product data is downloaded.
// ProductDataArgs is provided to me and I cannot modify it.
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
// this is where I get product name and add it to the list
string prodName = e.getProdName();
prodNames.Add(prodName);
// <-- I could show prodName here in a message box but that
// would show one message box for all 5 products. I don't
// want that. I want to show all 5 product names in one
// message box.
}
我需要在表单或消息框中显示所有5个产品名称。我可以在上面的活动中,但每个产品。这将显示5个消息框(或表单)。但我需要在一个消息框中显示所有5个。所以,我需要在调用者中这样的事情:
List<string> prodNames = await.GetProducts(ids);
var names = null;
foreach(var prod in prodNames)
{
names += string.Format("{0}\n");
}
MesageBox.Show(names);
问题:我遇到的问题是操作可能需要一些时间,而且我不知道列表何时加载了所有5个产品名称(因为名称在GetProducts方法中不可用,但在OnDownloadedProduct中事件)。
经过我自己的一些研究后,Tasks / async / await似乎为此提供了解决方案,但到目前为止我看到的所有示例都没有解决我上面描述的情况,我依赖于事件处理程序来获取我需要的东西(在这种情况下,所有5个产品名称。)
更新 感谢下面的@Kris评论,我已经能够将我的逻辑重写为此,但它无法正常工作:
public class ProductRetriver
{
List<int> ids = new List<int>(); // product ids
List<string> prodNames = new List<string>(); // product names
private TaskCompletionSource<List<string>> prodsTask;
this.EventHandler += OnDownloadedProduct;
Task<List<string>> async GetProducts(List<int> _ids)
{
ids = _ids;
prodsTask = new TaskCompletionSource<List<string>>();
foreach (var id in ids)
{
//PROBLEM: GetProduct() is not awaited, so method will finish
//before list is filled. Also, it seem to add duplicates for
//some reason to the list. I cannot modify the method as it is
//not code available to me.
downloader.GetProduct(id); //fires OnDownloadedProduct
}
return prodNames;
}
//this event is fired once each product data is downloaded
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
// this is where I get product name and add it to the list
string prodName = e.getProdName();
prodNames.Add(prodName);
if (prodNames.Count == ids.Count)
{
prodsTask.SetResult(prodNames);
}
}
}
答案 0 :(得分:1)
我不知道这个下载器是什么以及它来自哪里,如果eventhandler在外侧下载器试试这个
public class ProductRetriver
{
List<int> ids = new List<int>();
List<string> prodNames = new List<string>(); //list to fill with prod names once they are downloaded
private TaskCompletionSource<List<string>> prodsTask;
this.EventHandler += OnDownloadedProduct; //event fired once each product is downloaded
Task<List<string>> async GetProducts(List<int> _ids)
{
ids = _ids;
prodsTask = new TaskCompletionSource<List<string>>();
foreach (var id in ids)
{
downloader.GetProduct(id); //fires OnDownloadedProduct once product is downloaded
}
}
//this event is fired once each product data is downloaded
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
// this is where I get product name and add it to the list
string prodName = e.getProdName();
prodNames.Add(prodName);
if (prodNames.Count == ids.Count)
{
prodsTask.SetResult(prodNames);
}
}
}
如果eventhandler是下载程序的一部分,则可以执行此操作
Task<List<string>> async GetProducts(List<int> ids)
{
List<string> prodNames = new List<string>();
downloder.EventHandler += (s, e) => {
string prodName = e.getProdName();
prodNames.Add(prodName);
if (prodNames.Count == ids.Count)
{
prodsTask.SetResult(prodNames);
}
};
foreach (var id in ids)
{
downloader.GetProduct(id); //fires OnDownloadedProduct once product is downloaded
}
return prodNames;
}
你可以这样称呼
Products = await getproducts(ids);
答案 1 :(得分:0)
如果我理解,你不需要收到downloader.GetProduct(id)的结果吗?如果是这样 - 你想要等待什么?只需创建任务列表并等待它们全部完成如下:
public class ProductRetriver
{
List<int> ids = new List<int>(); // product ids
List<string> prodNames = new List<string>(); // product names
private TaskCompletionSource<List<string>> prodsTask;
this.EventHandler += OnDownloadedProduct;
public static List<Task> TaskList = new List<Task>();
Task<List<string>> async GetProducts(List<int> _ids)
{
ids = _ids;
prodsTask = new TaskCompletionSource<List<string>>();
foreach (var id in ids)
{
TaskList.Add(downloader.GetProduct(id));
}
Task.WaitAll(TaskList.ToArray());
return prodNames;
}
//this event is fired once each product data is downloaded
void OnDownloadedProduct(object sender, ProductDataArgs e)
{
// this is where I get product name and add it to the list
string prodName = e.getProdName();
prodNames.Add(prodName);
if (prodNames.Count == ids.Count)
{
prodsTask.SetResult(prodNames);
}
}
}