这真的是异步的吗?IEnumerable<T> 扩展

4
public static async Task<T> FirstIfNotNullOrEmptyAsync<T>(this Task<IEnumerable<T>> obj) where T : class
{
    var first = await Task.WhenAny(obj);

    return first?.Result?.FirstOrDefault() ?? null;
}

以下是另外两种显示完整内容的方法:

主要方法(在这种情况下,将仅返回集合中的一个项目或一个空集合)

private async Task<IEnumerable<T>> fetchRecordsAsync(SqlCommand SqlCommand, bool isSingleRecord)
{..some code...}

这里是获取集合并仅传递T的调用方:
protected async Task<T> GetSingleRecord(SqlCommand SqlCommand)
{
    return await fetchRecordsAsync(SqlCommand, true).FirstIfNotNullOrEmptyAsync();
}

这是使用IEnumerable的异步扩展的正确方式吗?还是它会执行同步操作?

我真的需要创建另一个任务(首先)吗?


1
你是指参数中的 this IEnumerable<Task<T>> obj 吗? - Clay
你会怎么称呼这个方法?如何传递Task<IEnumerable<T>>? - M.kazem Akhgary
您正在混合使用异步和同步代码,这是不好的。 - Nkosi
为什么不直接使用 return (await obj)?.FirstOrDefault(); 呢? - Yacoub Massad
2
“*?? null*”是多余的。 - Clay
显示剩余4条评论
2个回答

4

这真的是异步的吗?

是的,它是异步的。await Task.WhenAny将返回完成的第一个任务。在这样的任务(已完成的任务)上使用.Result不会导致同步等待。

顺便说一下,您不需要使用Task.WhenAny,您的方法可以简化为:

public static async Task<T> FirstIfNotNullOrEmptyAsync<T>(
    this Task<IEnumerable<T>> obj) where T : class
{
    var result = await obj;

    return result?.FirstOrDefault();
}

@Jonesopolis,“代表所提供的任务之一完成的任务”意味着一个未完成的任务(除非已经有一个输入任务已经完成)。它是一个当任何一个输入任务完成时将完成的任务。 - Yacoub Massad
2
因为 OP 只传递一个任务到 WhenAny(),所以不会有未完成的任务。但是确实在这里使用 WhenAny() 是没有意义的。 - JLRishe
@JLRishe,异步数据库访问在调用Task.WhenAny之前完成的可能性非常小。 - Yacoub Massad
@YacoubMassad 当然可以,但这时它已经完成了,因此调用Result是安全的。关键是你可以百分之百确定任务已经完成(无论成功与否),所以Result不会执行阻塞等待。它不可能返回一个未完成的任务。 - Servy
@Servy,你是对的。我犯了一个错误。回答已更新。 - Yacoub Massad

2
它将异步执行所有工作,尽管有很多不必要的内容(虽然除了非常小的额外开销之外没有害处)。
当您只传递一个任务时,调用WhenAny没有意义。您可以直接等待任务本身;它做的事情是一样的。
写“?? null”是无意义的。如果值为null,请使其为null。它已经是null,所以你可以把它留下来。
您也不会将空的Task传递给FirstIfNotNullOrEmptyAsync,您确实也不应该这样做。您也可能不应该为结果生成null IEnumerable。如果没有项目,您真的应该有一个空序列。
也没有理由限制该方法仅适用于类序列。如果有人想使用值类型,则没有真正的理由阻止他们。
调整方法的名称,以便其名称反映其实际执行的操作,这也是有意义的。
现在所有这些都可以简化为:
public static async Task<T> FirstOrDefaultAsync<T>(this Task<IEnumerable<T>> task)
{    
    return (await task).FirstOrDefault();
}

你的另一种方法也没有理由是async,因为除了重新包装它并返回一个Task之外,你从未对等待的值做任何事情。只需返回你拥有的Task

protected Task<T> GetSingleRecordAsync(SqlCommand SqlCommand)
{
    return fetchRecordsAsync(SqlCommand, true)
        .FirstOrDefaultAsync();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接