IAsyncEnumerable或IAsyncEnumerator的工厂 [英] Factory for IAsyncEnumerable or IAsyncEnumerator

查看:72
本文介绍了IAsyncEnumerable或IAsyncEnumerator的工厂的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想知道是否有一种方法可以通过Source对象(类似于 TaskCompletionSource 允许您执行某项任务.特别是, TaskCompletionSource 可以像其他任何参数一样传递.

也许是这样的:

 公共类AsyncEnumerables {公共任务HandlerTask {放;}公共异步Task< string>ParentMethod(){var source = new AsyncEnumerableSource< int>();IAsyncEnumerable asyncEnumerable = source.GetAsyncEnumerable();HandlerTask = Task.Run(()=> handleAsyncResultsAs他们发生(asyncEnumerable));int n =等待someOtherTask();source.YieldReturn(n);var r = await ChildMethod(source);source.Complete();//此调用将导致HandlerTask完成.返回r;}私有异步Task< string>ChildMethod(AsyncEnumerableSource< int>源){source.YieldReturn(5);等待SomeOtherCall();source.YieldReturn(10);返回你好";}} 

使用上述代码, handleAsyncResultsAs他们发生任务将看到传递给YieldReturn的任何值.因此,它将看到以上代码中的 n 以及 ChildMethod 中的 5 10 .

解决方案

如果您可以构建代码以利用 yield return await foreach

的优势,那么情况会好得多.代码>.例如,这段代码几乎可以做同样的事情:

 公共异步任务Consume(){var source = ParentMethod();HandlerTask = Task.Run(async()=> {等待foreach(源代码中的var项目){Console.WriteLine(item);}});}公共异步IAsyncEnumerable< int>ParentMethod(){等待Task.Yield();收益率13;等待foreach(ChildMethod()中的var项)退货项目;}私有异步IAsyncEnumerable< int>ChildMethod(){收益率5;等待Task.Yield();收益率10;} 

但是,如果您确实需要异步可枚举来源",则需要首先识别一件事. TaskCompletionSource< T> 保存结果,即 T (或例外).它充当容器.可以在等待任务之前设置结果.异步可枚举来源"是一回事-您需要它能够保留结果,然后再从中获取任何项目.异步可枚举来源"将需要保存多个结果-在这种情况下,是一个 collection .

实际上,您实际上要问的是可以作为异步枚举使用的集合".这里有几种可能,但我推荐的是一种 Channel :

 公共异步任务< string>ParentMethod(){var source = Channel.CreateUnbounded< int>();var sourceWriter = source.Writer;IAsyncEnumerable< int>asyncEnumerable = source.Reader.ReadAllAsync();HandlerTask = Task.Run(async()=> {等待foreach(asyncEnumerable中的var项目)Console.WriteLine(item);});等待Task.Yield();等待sourceWriter.WriteAsync(13);var r = await ChildMethod(sourceWriter);sourceWriter.Complete();返回r;}私有异步Task< string>ChildMethod(ChannelWriter< int> sourceWriter){等待sourceWriter.WriteAsync(5);等待Task.Yield();等待sourceWriter.WriteAsync(10);返回你好";} 

I'm wondering if there is a way to create either IAsyncEnumerable<T> or IAsyncEnumerator<T> via a Source object, rather like TaskCompletionSource allows one to do for tasks. In particular, TaskCompletionSource can be passed around like any other parameter.

Maybe something like this:

public class AsyncEnumerables {

    public Task HandlerTask { get; set; }

    public async Task<string> ParentMethod() {
        var source = new AsyncEnumerableSource<int>();
        IAsyncEnumerable asyncEnumerable = source.GetAsyncEnumerable();
        HandlerTask = Task.Run(() => handleAsyncResultsAsTheyHappen(asyncEnumerable));
        int n = await someOtherTask();
        source.YieldReturn(n);
        var r = await ChildMethod(source);
        source.Complete();  // this call would cause the HandlerTask to complete.
        return r;
    }

    private async Task<string> ChildMethod(AsyncEnumerableSource<int> source) {
        source.YieldReturn(5);
        await SomeOtherCall();
        source.YieldReturn(10);
        return "hello";
    }
}

With the above code, the handleAsyncResultsAsTheyHappen task would see whatever values got passed into YieldReturn. So it would see the n from the above code, as well as the 5 and the 10 from ChildMethod.

解决方案

You're much better off if you can structure your code to take advantage of yield return and await foreach. E.g., this code does almost the same thing:

public async Task Consume()
{
    var source = ParentMethod();
    HandlerTask = Task.Run(async () => { await foreach (var item in source) { Console.WriteLine(item); } });
}

public async IAsyncEnumerable<int> ParentMethod()
{
    await Task.Yield();
    yield return 13;
    await foreach (var item in ChildMethod())
        yield return item;
}

private async IAsyncEnumerable<int> ChildMethod()
{
    yield return 5;
    await Task.Yield();
    yield return 10;
}

However, if you really need an "async enumerable source", you need to first recognize one thing. TaskCompletionSource<T> holds the results, i.e., the T (or exception). It's acting as a container. The result can be set before the task is awaited. It's the same thing with the "async enumerable source" - you'd need it to be able to hold results before any items are taken from it. The "async enumerable source" would need to hold multiple results - in this case, a collection.

So what you're actually asking for is "a collection that can be consumed as an asynchronous enumerable". There are a few possibilities here, but the one I'd recommend is a Channel:

public async Task<string> ParentMethod()
{
  var source = Channel.CreateUnbounded<int>();
  var sourceWriter = source.Writer;
  IAsyncEnumerable<int> asyncEnumerable = source.Reader.ReadAllAsync();
  HandlerTask = Task.Run(async () => { await foreach (var item in asyncEnumerable) Console.WriteLine(item); });
  await Task.Yield();
  await sourceWriter.WriteAsync(13);
  var r = await ChildMethod(sourceWriter);
  sourceWriter.Complete();
  return r;
}

private async Task<string> ChildMethod(ChannelWriter<int> sourceWriter)
{
  await sourceWriter.WriteAsync(5);
  await Task.Yield();
  await sourceWriter.WriteAsync(10);
  return "hello";
}

这篇关于IAsyncEnumerable或IAsyncEnumerator的工厂的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆