如果一个方法要回传IAsyncEnumerable ,而方法内部使用yield return 该怎么写呢?
我们一样就拿ReadLineAsync 来示范,首先建立一个类别实作IAsyncEnumerator ,当然这也包含了实作IAsyncDisposable:

点击(此处)折叠或打开

  1. internal class AsyncEnumerator : IAsyncEnumerator<string>
  2.     {
  3.         private readonly StreamReader _reader;

  4.         private bool _disposed;

  5.         public string Current { get; private set; }

  6.         public AsyncEnumerator(string path)
  7.         {
  8.             _reader = File.OpenText(path);
  9.             _disposed = false;
  10.         }
  11.         async public ValueTask<bool> MoveNextAsync()
  12.         {
  13.             var result = await _reader.ReadLineAsync();
  14.             Current = result;
  15.             return result != null;
  16.         }
  17.         async public ValueTask DisposeAsync()
  18.         {
  19.             await Task.Run(() => Dispose());
  20.         }

  21.         private void Dispose()
  22.         {
  23.             Dispose(true);
  24.             GC.SuppressFinalize(this);
  25.         }

  26.         private void Dispose(bool disposing)
  27.         {
  28.             if (!this._disposed)
  29.             {
  30.                 if (_reader != null)
  31.                 {
  32.                     _reader.Dispose();
  33.                 }
  34.                 _disposed = true;
  35.             }
  36.         }
  37.     }

接着建立另外一个类别, 这个类别很简单,只包含一个静态的方法async static public IAsyncEnumerable ReadLineAsync(string path),实作内容如下:

点击(此处)折叠或打开

  1. async static public IAsyncEnumerable<string> ReadLineAsync(string path)
  2.         {

  3.             var enumerator = new AsyncEnumerator(path);
  4.             try
  5.             {
  6.                 while (await enumerator.MoveNextAsync())
  7.                 {
  8.                     await Task.Delay(100);
  9.                     yield return enumerator.Current;
  10.                 }
  11.             }
  12.             finally
  13.             {
  14.                 await enumerator.DisposeAsync();
  15.             }
  16.         }
  17.     }
程式码没有错,但编译过不了,观察一下错误讯息:

点击(此处)折叠或打开

  1. 错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.GetResult'
  2. 错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.GetStatus'
  3. 错误CS0656:缺少编译器所需的成员'系统。 Threading.Tasks.ManualResetValueTaskSourceLogic`1.get_Version'
  4. 错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.OnCompleted'
  5. 错误CS0656:缺少编译器所需成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.Reset'
  6. 错误CS0656:缺少编译器所需的成员'System.Threading.Tasks.ManualResetValueTaskSourceLogic`1.SetException'
  7. 错误CS0656:缺少编译器所需的成员'System.Threading.Tasks.ManualResetValueTaskLogic.cn
  8. .SetResult ' 错误CS0656:缺少编译器所需的成员'System.Runtime.CompilerServices.IStrongBox`1.get_Value'
  9. 错误CS0656:缺少编译器所需的成员

很明显,编译器需要两个型别(1) System.Threading.Tasks.ManualResetValueTaskSourceLogic (2) System.Runtime.CompilerServices.IStrongBox才能完成编译。感谢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 and IStrongBox) , while the libraries include the approved API surface area (ManualResetValueTaskSourceCore), and we didn't have time to get the compiler updated.』,简单说就是编译器和框架目前的更新进度不一致,导致少了点什么。既然如此,我们就遵照本草纲目的指示,补上这两个型别,请注意,这两个型别的命名空间必须正确:

点击(此处)折叠或打开

  1. using System;
  2. using System.Runtime.CompilerServices;
  3. using System.Threading.Tasks.Sources;


  4. namespace System.Threading.Tasks{
  5.    
  6.     internal struct ManualResetValueTaskSourceLogic<TResult>
  7.     {
  8.         private ManualResetValueTaskSourceCore<TResult> _core;
  9.         public ManualResetValueTaskSourceLogic(IStrongBox<ManualResetValueTaskSourceLogic<TResult>> parent) : this() { }
  10.         public short Version => _core.Version;
  11.         public TResult GetResult(short token) => _core.GetResult(token);
  12.         public ValueTaskSourceStatus GetStatus(short token) => _core.GetStatus(token);
  13.         public void OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _core.OnCompleted(continuation, state, token, flags);
  14.         public void Reset() => _core.Reset();
  15.         public void SetResult(TResult result) => _core.SetResult(result);
  16.         public void SetException(Exception error) => _core.SetException(error);
  17.     }
  18. }

  19. namespace System.Runtime.CompilerServices
  20. {
  21.     internal interface IStrongBox<T> { ref T Value { get; } }
  22. }

补上去后就大功告成,可以快乐地非同步yielld return。
09-04 19:37