我如何编写自己的FirstOrDefaultAsync()方法

时间:2015-02-19 08:53:58

标签: c# .net asynchronous task-parallel-library async-await

我开始关注异步/等待编程模型并尝试理解模式以及如何使用它们。

鉴于我有一个返回Task<List<string>> GetListAsync()

的方法

如何编写我自己的FirstOrDefault()方法

Task<string> result = GetListAsync().FirstOrDefaultAsync();

这感觉不对:

 var resultList = await GetListAsync();
 var firstElementTask = Task.FromResult(resultList.FirstOrDefault());

有什么方法可以打破async / await编程模型?

3 个答案:

答案 0 :(得分:4)

当您的方法返回Task<List<string>>时,创建FirstOrDefaultAsync并没有多大意义。你当然可以,但它没有多大帮助。

因为在任务完成之前您无法获得List<string>。任务完成后,您将留下内存集合中的List<string>。它拥有您需要的所有元素。此时,您只需拨打yourList.FirstOrDefault()

另一方面,当您拥有FirstOrDefaultAsyncList<Task<string>>时,有必要创建IEnumerable<Task<string>>

public async Task<T> FirstOrDefaultAsync<T>(IEnumerable<Task<T>> tasks)
{
    var first = await Task.WhenAny(tasks);
    return await first;
}

正如@ i3arnon在评论中指出的那样,这个方法返回任务的结果,无论哪个先发生。不是列表中的第一个。

答案 1 :(得分:3)

您应该意识到将FirsOrDefaultAsync置于GetListAsync之上意味着当异步操作完成时,将实现整个列表。 (即Task<List<string>>已完成并导致List<string>

即使拥有FirsOrDefaultAsync,也没有真正的用处。它总是等同于:

var list = await GetListAsync();
var result = list.FirstOrDefault();

拥有真正的异步FirsOrDefaultAsync的唯一方法是让提供者支持只异步查询第一个项而不是整个列表。

LINQ和async-await之间存在固有冲突,因为它们都依赖编译器将代码转换为状态机。这就是Reactive Extension存在的原因(可能值得一试)。另一个很好的解决方案可以在MongoDB C#驱动程序中找到,他们将IEnumerable重新发送为IAsyncCursor

如果可以,你想要的只是辅助方法,那么就是这样:

public async Task<string> FirstOrDefaultAsync()
{
    var list = await GetListAsync();
    return list.FirstOrDefault();
}

答案 2 :(得分:1)

您的方法返回Task<List<x>>,因此当任务完成时,您会获得一个列表。期。第二个事实:您正在使用等待获取列表。这使得调用方法已经异步。您不需要在任务中手动包装任何内容:

var resultList = await GetListAsync()
return resultList.FirstOrDefault();

应该是它。

然而,这当然是STILL获取整个列表。但这是&#34; GetList&#34;设计中的一个缺陷。方法。由于它返回一个列表,因此没有什么可以做的。除非List具有延迟实现,否则它将始终返回整个列表。

如果您可以控制该片段代码,您可以尝试重写GetListAsync以返回IEnumerable / IQueryable并确保它们被懒惰处理,但这是另一个主题。