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。故事还没完,待续........

原文地址:https://www.cnblogs.com/wwwblender-3dcn/p/10151950.html