异步迭代器任务<ienumerable <t>&gt; </ienumerable <t>

时间:2014-04-25 13:51:51

标签: c# .net asynchronous iterator clr

我正在尝试实现一个返回迭代器的异步函数。这个想法如下:

    private async Task<IEnumerable<char>> TestAsync(string testString)
    {
        foreach (char c in testString.ToCharArray())
        {
            // do other work
            yield return c;
        }
    }

但是,有一条错误消息,该函数不能是迭代器块,因为Task<IEnumerable<char>>不是迭代器接口类型。有解决方案吗?

3 个答案:

答案 0 :(得分:20)

听起来你可能真正想要的是类似IObservable<T>的东西,它有点像基于推送的异步IEnumerable<T>。请参阅Reactive Extensions, a.k.a. Rx(Apache-2.0下许可的代码)(无联属关系),了解与IObservable<T>一起使用的大量方法,使其像LINQ-to-Objects一样工作。

IEnumerable<T>的问题在于,没有什么能真正使枚举本身异步。如果您不想在Rx上添加依赖项(这实际上是IObservable<T>闪耀的原因),这个替代方案可能对您有用:

public async Task<IEnumerable<char>> TestAsync(string testString)
{
    return GetChars(testString);
}

private static IEnumerable<char> GetChars(string testString)
{
    foreach (char c in testString.ToCharArray())
    {
        // do other work
        yield return c;
    }
}

虽然我想指出,如果不知道实际上是异步完成的,那么可能有更好的方法来实现您的目标。您发布的所有代码都不会异步执行任何操作,我也不知道// do other work中的任何内容是否异步(在这种情况下,这不是解决您的根本问题的方法,尽管它会让你的代码编译)。

答案 1 :(得分:13)

要详细说明以前的答案,您可以使用Reactive Extensions的Observable.Create<TResult>系列方法来完成您想要的操作。

以下是一个例子:

var observable = Observable.Create<char>(async (observer, cancel) =>
{
    for (var i = 0; !cancel.IsCancellationRequested && i < 100; i++)
    {
        observer.OnNext(await GetCharAsync());
    }
});

以下是在LINQPad中使用它的方法,例如:

// Create a disposable that keeps the query running.
// This is necessary, since the observable is 100% async.
var end = Util.KeepRunning();

observable.Subscribe(
    c => Console.WriteLine(c.ToString()),
    () => end.Dispose());

答案 2 :(得分:9)

A more "batteries-included" implementation这类事情,包括语言支持,目前(在撰写本文时)被considered包含在C#8.0中。

据我了解,这可能随时更改。