C# 8.0 抢先看-- Async Stream
在目前版本中非同步迭代使用yield return 的暂时解决方案说明。
开发环境Visual Studio 2019 Preview 1 (16.0.0 Preview 1)
框架.NET Core 3.0.0-preview-27122-01
编译器C# 8.0 beta
上一篇简单示范了在类别中实作Async Stream 的方式, 如果今天是一个方法要回传IAsyncEnumerable<T> ,而方法内部使用yield return 该怎么写呢?
我们一样就拿ReadLineAsync 来示范,首先建立一个类别实作IAsyncEnumerator<T> ,当然这也包含了实作IAsyncDisposable:
internal class AsyncEnumerator : IAsyncEnumerator<string> { private readonly StreamReader _reader; private bool _disposed; public string Current { get; private set; } public AsyncEnumerator(string path) { _reader = File.OpenText(path); _disposed = false; } async public ValueTask<bool> MoveNextAsync() { var result = await _reader.ReadLineAsync(); Current = result; return result != null; } async public ValueTask DisposeAsync() { await Task.Run(() => Dispose()); } private void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this._disposed) { if (_reader != null) { _reader.Dispose(); } _disposed = true; } } }
接着建立另外一个类别, 这个类别很简单,只包含一个静态的方法async static public IAsyncEnumerable<string> ReadLineAsync(string path),实作内容如下:
async static public IAsyncEnumerable<string> ReadLineAsync(string path) { var enumerator = new AsyncEnumerator(path); try { while (await enumerator.MoveNextAsync()) { await Task.Delay(100); yield return enumerator.Current; } } finally { await enumerator.DisposeAsync(); } } }
程式码没有错,但编译过不了,观察一下错误讯息:
错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.GetResult'
错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.GetStatus'
错误CS0656:缺少编译器所需的成员'系统。 Threading.Tasks.ManualResetValueTaskSourceLogic`1.get_Version'
错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.OnCompleted'
错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.Reset'
错误CS0656:缺少编译器所需的成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.SetException'
错误CS0656:缺少编译器所需的成员'System.Threading.Tasks.ManualResetValueTaskLogic.cn
.SetResult ' 错误CS0656:缺少编译器所需的成员'System.Runtime.CompilerServices.IStrongBox`1.get_Value'
错误CS0656:缺少编译器所需的成员'系统。 Runtime.CompilerServices.IStrongBox`1.Value”
很明显,编译器需要两个型别(1) System.Threading.Tasks.ManualResetValueTaskSourceLogic<T> (2) System.Runtime.CompilerServices.IStrongBox<T>才能完成编译。感谢open source与git hub,在微软的dotnet/corclr的专案中找到了这么一段讨论~~ ManualResetValueTaskSourceLogic`1 missing in System.Private.CoreLib #21379,有位stephentoub (应该是微软员工而且是这个专案的成员)提到『It's not missing exactly, but like @benaadams said things are just out-of-sync between the compiler and library in Preview 1. The compiler is looking for the old design (ManualResetValueTaskSourceLogic<T> and IStrongBox<T>) , while the libraries include the approved API surface area (ManualResetValueTaskSourceCore<T>), and we didn't have time to get the compiler updated.』,简单说就是编译器和框架目前的更新进度不一致,导致少了点什么。既然如此,我们就遵照本草纲目的指示,补上这两个型别,请注意,这两个型别的命名空间必须正确:
using System; using System.Runtime.CompilerServices; using System.Threading.Tasks.Sources; namespace System.Threading.Tasks{ internal struct ManualResetValueTaskSourceLogic<TResult> { private ManualResetValueTaskSourceCore<TResult> _core; public ManualResetValueTaskSourceLogic(IStrongBox<ManualResetValueTaskSourceLogic<TResult>> parent) : this() { } public short Version => _core.Version; public TResult GetResult(short token) => _core.GetResult(token); public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token); public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _core.OnCompleted(continuation, state, token, flags); public void Reset() => _core.Reset(); public void SetResult(TResult result) => _core.SetResult(result); public void SetException(Exception error) => _core.SetException(error); } } namespace System.Runtime.CompilerServices { internal interface IStrongBox<T> { ref T Value { get; } } }
补上去后就大功告成,可以快乐地非同步yielld return。故事还没完,待续........
posted on 2018-12-20 20:02 www.blender-3d.cn 阅读(452) 评论(0) 编辑 收藏 举报