【问题标题】:Has this usage of async / await in C# been discovered before? [closed]以前是否发现过 C# 中 async / await 的这种用法? [关闭]
【发布时间】:2012-10-12 08:20:30
【问题描述】:

在 stackoverflow 上关于 async / await 的上一个问题之后,在我看来,await 比市场营销建议的更强大和更通用。它似乎是一种构建计算表达式的通用方法,就像在 F# 中一样。因此,经过一番挣扎,我想出了一些成功执行的代码,如下所示。

    using FluentAssertions;
    using System.Collections.Generic;

    namespace EnumerableViaAwait.Specs
    {
        [global::Microsoft.VisualStudio.TestTools.UnitTesting.TestClass]
        public class MyTestClass
        {
            public IEnumerable<int> Numbers()
            {
                return EnumeratorMonad.Build<int>(async Yield =>
                {
                    await Yield(11);
                    await Yield(22);
                    await Yield(33);
                });
            }

            [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod]
            public void TestEnum()
            {
                var v = Numbers();
                var e = v.GetEnumerator();

                int[] expected = { 11, 22, 33 };

                Numbers().Should().ContainInOrder(expected);

            }

        }
    }

现在仔细注意这里发生了什么。我构建了一个反应性的 observable。我正在构建一个 IEnumerable。它严格来说是一个拉动系统。我可以很高兴地写。

    foreach item in Numbers(){
            Console.WriteLine(item);
    }

它会打印出来

    11
    22
    33

这很有趣,因为系统不是严格异步的,但我滥用了 await 框架和“等待任何东西”的能力,如此处所述。 http://blogs.msdn.com/b/pfxteam/archive/2011/01/13/10115642.aspx.The 问题是。

  1. 这种对 await / async 的滥用能走多远?
  2. 这种模式是否与 F# 计算表达式一样强大
  3. 传统的 IEnumerator / Yield 模式只是评估的语法糖吗 完全符合这种模式

实现该模式的代码如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading.Tasks;

    namespace EnumerableViaAwait
    {

        public class EnumeratorMonad<T> : IEnumerable<T>, IEnumerator<T>
        {
            public class Yield
            {
                private EnumeratorMonad<T> _Monad;

                public Yield(EnumeratorMonad<T> monad)
                {
                    _Monad = monad;
                }

                public YieldAwaiter GetAwaiter()
                {
                    return new YieldAwaiter(_Monad);
                }
            }

            public class YieldAwaiter : INotifyCompletion
            {
                EnumeratorMonad<T> _Monad;

                public YieldAwaiter(EnumeratorMonad<T> monad)
                {
                    _Monad = monad;
                }

                public bool IsCompleted
                {
                    get { return _Monad.IsCompleted(); }
                }

                public void GetResult()
                { }

                public void OnCompleted(Action continuation)
                {
                    _Monad.Next = continuation; 
                }

            }

            private bool Completed { get; set; }

            public EnumeratorMonad()
            {
                Completed = false;
            }

            public bool IsCompleted()
            {
                return Completed;
            }

            public void Build(Func<Func<T, Yield>, Task> action)
            {
                Func<T, Yield> yielder = (T value) => { 
                    _Current = value;
                    return new Yield(this);
                };
                Next = async () => { 
                    await action(yielder);
                    Completed = true;
                };
            }

            private T _Current;
            public T Current
            {
                get { return _Current; }
            }

            public void Dispose()
            {
            }

            object System.Collections.IEnumerator.Current
            {
                get { return _Current; }
            }


            Action Next;
            public bool MoveNext()
            {
                if (!Completed )
                {
                    Next();
                }
                return !Completed;
            }

            public void Reset()
            {
                throw new NotImplementedException();
            }



            IEnumerator<T> IEnumerable<T>.GetEnumerator()
            {
                return this;
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this;
            }
        }

        public class EnumeratorMonad{
            public static EnumeratorMonad<T> Build<T>(Func<Func<T, EnumeratorMonad<T>.Yield>, Task> action)
            {
                var monad = new EnumeratorMonad<T>();
                monad.Build(action);
                return monad;
            }
        }

}

【问题讨论】:

  • 你的问题是......?
  • 问题在中间。我猜你没看中间吧?
  • +1 发现这个特别有趣。 :)
  • @bradgonesurfing,一行中有 4 个空格的前缀会使其像代码一样格式化。你知道……对吗?
  • 您可能对Jon Skeet's eduasync posts感兴趣。

标签: c# asynchronous visual-studio-2012 monads async-await


【解决方案1】:

yield returnawait/async 只是协程的不同特殊形式。你已经证明你可以(基本上)使用await/async 来实现yield return,我不会惊讶地发现反过来也可能。我确信它们的实现方式非常相似。

在实践中,当然,我不会使用await/async 进行迭代,因为yield return 更简单、更清晰。

所以,

  1. 您可以尽情享受这种“虐待”。
  2. 对 F# 不够熟悉,无法回答。
  3. 不,但 IIRC 功能的实现方式或多或少相同。

【讨论】:

  • 我想弄清楚的是 async/await 是“专门的”还是完全通用的,并且可用于实现比 IEnumerator 或 Async 更有趣的工作流。我不认为你可以在 yield return 方面实现 async / await,因为 yield return 不返回左值。
  • 您可能是对的,因为您无法以另一种方式获得漂亮的语法。至于你能用它们做什么——可能是任何东西,但最好的判断方法是尝试一下。
  • 接下来我要试试 Maybe monad。我想我可以写一些像 Do notation 这样的东西。如果返回了任何 Maybe::Nothings,则该序列应以 Maybe::Nothing 提前终止。它不是超级漂亮,但它可能对解析 XML 有好处
  • 这种模式的一个 巨大 好处 - 一个可能弥补减少的好处是可读性 - 它允许您创建匿名可枚举生成器(如给定的示例代码),yield return 关键字不允许。
猜你喜欢
  • 2023-03-27
  • 2015-11-16
  • 1970-01-01
  • 1970-01-01
  • 2017-06-30
  • 2019-04-30
  • 2022-01-11
  • 2019-04-25
  • 2016-01-09
相关资源
最近更新 更多