【问题标题】:Failing to write async recursive iteration function无法编写异步递归迭代函数
【发布时间】:2021-06-08 06:21:25
【问题描述】:

我正在尝试编写一个递归方法来检索对象的父对象(以及该父对象等)。这本身不是问题,但调用是异步的,这会导致以下错误:

'.....Recursive(string)' 的主体不能是迭代器块,因为'Task' 不是迭代器接口类型 [...]csharp(CS1624)

代码:

private async Task<IEnumerable<string>> Recursive(string objectId)
{
    var result = await GetParent(objectId);
    if (result?.Length > 0)
    {
        yield return result;
        await Recursive(objectId);
    }
}

private async Task<string> GetParent(string objectId)
{
    await Task.Run(() => { return $"{objectId}/parent"; });
}

我也尝试过IAsyncEnumerable,但这导致了以下错误:

“IAsyncEnumerable”不包含“GetAwaiter”的定义,并且找不到接受“IAsyncEnumerable”类型的第一个参数的可访问扩展方法“GetAwaiter”(您是否缺少 using 指令或程序集引用?)[。 ..]csharp(CS1061)

private async IAsyncEnumerable<string> Recursive(string objectId)
{
    var result = await GetParent(objectId);
    if (result?.Length > 0)
    {
        yield return result;
        await Recursive(objectId);
    }
}

private async Task<string> GetParent(string objectId)
{
    await Task.Run(() => { return $"{objectId}/parent"; });
}

我要写一个while循环来让它工作。但我对这是否可能很感兴趣。

更新 2: 好的,我想我明白了。谢谢大家。

private async IAsyncEnumerable<string> Recursive(string objectId)
{
    var result = await GetParent(objectId);
    if (result?.Length > 0)
    {
        yield return result;
        await foreach (var r2 in Recursive(objectId))
        {
            yield return r2;
        }
    }
}

private async Task<string> GetParent(string objectId)
{
    await Task.Run(() => { return $"{objectId}/parent"; });
}

【问题讨论】:

  • Task&lt;IEnumerable&lt;string&gt;&gt; 类型承诺消费者可能需要等待一段时间,但是一旦等待结束,消费者将能够毫不拖延地获取所有元素。您的方法没有这样做:在产生结果之后,可能会进一步延迟。您确实需要IAsyncEnumerable,它允许连续元素之间的延迟。但是,您没有向我们提供任何有关“导致更多问题”的信息,因此很难提供帮助
  • 错误很明显——你不能使用yield,除非方法返回IEnumerableIAsyncEnumerable. 这个实现不起作用。您对 IAsyncEnumerable 做了什么尝试? PS您写的内容也不适用于IEnumerable。您不能只从迭代器内部返回 IEnumerable
  • 先尝试创建一个适用于IEnumerable的递归方法,然后再将其转换为使用IAsyncEnumerable
  • 我不确定 async 是否相关,您不能按照您尝试的方式编写递归迭代函数,至少在我正确理解意图的情况下。迭代器块和递归不能很好地混合。如果可能的话,最好只编写一个迭代解决方案,或者为每个元素创建一个带有回调的递归解决方案。演示实际问题的示例可能会有所帮助。
  • 一会儿,我将添加 IAsyncEnumerable 尝试..

标签: c# asynchronous recursion


【解决方案1】:

当前代码即使是同步也不会编译,结果是IEnumerable&lt;string&gt;Recursive 的结果永远不会返回。也不可能只从迭代器返回 IEnumerable

这段代码可以工作。它是否有用是另一回事:

private IEnumerable<string> Recursive(string objectId)
{
    var result = GetParent(objectId);
    if (!string.IsNullOrEmpty(result))
    {
        yield return result;
        foreach(var r in Recursive(result))
        {
            yield return r;
        }
    }
}

private string GetParent(string objectId)
{
    return $"{objectId}/parent";
    
}

让它异步工作只需要更改为IAsyncEnumerable并使用await

private async IAsyncEnumerable<string> Recursive(string objectId)
{
    var result = await GetParent(objectId);
    if (!string.IsNullOrEmpty(result))
    {
        yield return result;
        await foreach(var r in Recursive(result))
        {
            yield return r;
        }
    }
}

private  Task<string> GetParent(string objectId)
{
    return Task.FromResult($"{objectId}/parent");
}

【讨论】:

  • 虽然这可能是所要求的,但假设迭代是有限的,它应该导致创建 n 个迭代器对象,并调用 n^2 个MoveNext 方法。 IE。不理想。
  • @JonasH 做什么?我们不知道实际的问题是什么。代码本身仅将/parent 附加到原始字符串,这是一个简单的循环可以做的事情和previous +="/parent"。如果我们知道真正的问题是什么,我们可以想出更好的解决方案
  • 对用例的一些见解。我有一个(SAP)组织,它有一个父级(这是有限的)。我需要从所述组织组装一个分层列表,直到找到根父级。最大深度为 5 或 6 步深。
  • 我同意这可能是所要求的。但是,如果事情是一个糟糕的主意,指出通常是个好主意。我能想到的情况很少,如果有的话,在同一个函数中迭代递归函数的结果是解决问题的最佳方法。
猜你喜欢
  • 1970-01-01
  • 2016-09-22
  • 2014-01-16
  • 2012-07-01
  • 1970-01-01
  • 2016-04-07
  • 2019-04-03
  • 2016-03-01
  • 2018-06-05
相关资源
最近更新 更多