收益率工作模式

发布于 2024-08-09 04:54:53 字数 824 浏览 2 评论 0原文

当我有一个代码块时,

static void Main()
{

  foreach (int i in YieldDemo.SupplyIntegers())
  {
    Console.WriteLine("{0} is consumed by foreach iteration", i);
  }
}


 class YieldDemo
  {
    public static IEnumerable<int> SupplyIntegers()
     {
         yield return 1;
         yield return 2;
          yield return 3;
       }
   }

我可以将yield return背后的原理解释为

  1. Main()调用SupplyIntegers()
  2. |1| |2| |3|存储在连续的内存块中。“IEnumerator”的指针移动到 |1|
  3. 控制从 SupplyInteger() 返回到 Main()。
  4. Main() 打印值
  5. Pointer Moves to |2|,依此类推。

说明:

(1) 通常我们会在函数内部允许有一个有效的 return 语句。 当存在多个yield return、yield return、...语句时,C# 会如何处理?

(2) 一旦遇到返回,控制就无法再次返回 SupplyIntegers(),如果允许,Yield 不会再次从 1 开始吗?我的意思是收益率1?

When i have a code block

static void Main()
{

  foreach (int i in YieldDemo.SupplyIntegers())
  {
    Console.WriteLine("{0} is consumed by foreach iteration", i);
  }
}


 class YieldDemo
  {
    public static IEnumerable<int> SupplyIntegers()
     {
         yield return 1;
         yield return 2;
          yield return 3;
       }
   }

can i interpret the principle behind yield return as

  1. Main() calls the SupplyIntegers()
  2. |1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
  3. Control returns from SupplyInteger() to Main().
  4. Main() prints the value
  5. Pointer Moves to |2|, and so on.

Clarifications :

(1) Normally we will have one valid return statement is allowed inside a function.How does
C# treats when multiple yield return ,yield return,... statements are present?

(2) Once the return is encountered there is no way for control again coming back to
SupplyIntegers(), in case it is allowed won't the Yield again starts from 1 ? I mean yield return 1?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

弄潮 2024-08-16 04:54:53

不——远非如此;我给你写一个长手写的版本……太蹩脚了!


请注意,如果您了解 foreach 实际上是有帮助的:

using(var iterator = YieldDemo.SupplyIntegers().GetEnumerator()) {
    int i;
    while(iterator.MoveNext()) {
        i = iterator.Current;
         Console.WriteLine("{0} is consumed by foreach iteration", i);
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
static class Program
{
    static void Main()
    {

        foreach (int i in YieldDemo.SupplyIntegers())
        {
            Console.WriteLine("{0} is consumed by foreach iteration", i);
        }
    }
}

 class YieldDemo
  {

    public static IEnumerable<int> SupplyIntegers()
     {
         return new YieldEnumerable();
       }
    class YieldEnumerable : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            return new YieldIterator();
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
    class YieldIterator : IEnumerator<int>
    {
        private int state = 0;
        private int value;
        public int Current { get { return value; } }
        object IEnumerator.Current { get { return Current; } }
        void IEnumerator.Reset() { throw new NotSupportedException(); }
        void IDisposable.Dispose() { }
        public bool MoveNext()
        {
            switch (state)
            {
                case 0: value = 1; state = 1;  return true;
                case 1: value = 2; state = 2;  return true;
                case 2: value = 3; state = 3; return true;
                default: return false;
            }
        }
    }
}

如您所见,它在迭代器中构建一个状态机,状态机通过 MoveNext 进行。我使用了带有 state 字段的模式,您可以看到这对于更复杂的迭代器是如何工作的。

重要的是:

  • 如果你有一个finally块(包括using),迭代器块中的任何变量都会成为状态机上的字段
  • ,它会进入 Dispose()
  • 导致 yield return
  • 部分代码变成 case (粗略地) yield break 变成 <代码>状态=-1; return false; (或类似)

C# 编译器执行此操作的方式非常复杂,但它使编写迭代器变得轻而易举。

Nope - far from it; I'll write a long-hand version for you... it is too grungy!


Note it also helps if you understand that the foreach is actually:

using(var iterator = YieldDemo.SupplyIntegers().GetEnumerator()) {
    int i;
    while(iterator.MoveNext()) {
        i = iterator.Current;
         Console.WriteLine("{0} is consumed by foreach iteration", i);
    }
}

using System;
using System.Collections;
using System.Collections.Generic;
static class Program
{
    static void Main()
    {

        foreach (int i in YieldDemo.SupplyIntegers())
        {
            Console.WriteLine("{0} is consumed by foreach iteration", i);
        }
    }
}

 class YieldDemo
  {

    public static IEnumerable<int> SupplyIntegers()
     {
         return new YieldEnumerable();
       }
    class YieldEnumerable : IEnumerable<int>
    {
        public IEnumerator<int> GetEnumerator()
        {
            return new YieldIterator();
        }
        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
    class YieldIterator : IEnumerator<int>
    {
        private int state = 0;
        private int value;
        public int Current { get { return value; } }
        object IEnumerator.Current { get { return Current; } }
        void IEnumerator.Reset() { throw new NotSupportedException(); }
        void IDisposable.Dispose() { }
        public bool MoveNext()
        {
            switch (state)
            {
                case 0: value = 1; state = 1;  return true;
                case 1: value = 2; state = 2;  return true;
                case 2: value = 3; state = 3; return true;
                default: return false;
            }
        }
    }
}

As you can see, it builds a state machine in the iterator, with the state machine progressed by MoveNext. I've used the pattern with a state field, as you can see how this would work for more complex iterators.

Importantly:

  • any variables in your iterator block become fields on the state machine
  • if you have a finally block (including using), it goes in the Dispose()
  • portions of code leading to a yield return become a case (roughly)
  • yield break becomes a state = -1; return false; (or similar)

The way the C# compiler does this is very complicated, but it makes writing iterators a breeze.

℡寂寞咖啡 2024-08-16 04:54:53

它只是一个语法糖,.net 为您生成 IEnumerator 类并实现 MoveNext、Current 和 Reset 方法,然后生成 IEnumarable 类 GetEnumerator,其中返回 IEnumerator,您可以通过 .net Reflector 或 ildasm 看到这些魔术类。

另请参阅此处

It's just a syntax sugar, .net generates IEnumerator class for you and implements MoveNext, Current and Reset methods, than generates IEnumarable class GetEnumerator of which returns that IEnumerator, you can see that magic classes by .net reflector or ildasm.

Also see here

苦妄 2024-08-16 04:54:53

简而言之,迭代器块(或者带有 yield 语句的方法,如果可以的话)被编译器转换为编译器生成的类。此类实现 IEnumeratoryield 语句转换为该类的“状态”。

例如,这:

yield return 1;
yield return 2;
yield return 3;

可能会转换成类似于:

switch (state)
{
    case 0: goto LABEL_A;
    case 1: goto LABEL_B;
    case 2: goto LABEL_C;
}
LABEL_A:
    return 1;
LABEL_B:
    return 2;
LABEL_C:
    return 3;

迭代器块可以被视为抽象状态机。此代码将由 IEnumerator 的方法调用。

Simply put, iterator blocks (or methods with yield statements, if you may) are transformed by the compiler into a compiler-generated class. This class implements IEnumerator and the yield statement is transformed into a 'state' for that class.

For instance, this:

yield return 1;
yield return 2;
yield return 3;

might get transformed into something similar to:

switch (state)
{
    case 0: goto LABEL_A;
    case 1: goto LABEL_B;
    case 2: goto LABEL_C;
}
LABEL_A:
    return 1;
LABEL_B:
    return 2;
LABEL_C:
    return 3;

Iterator blocks are can be seen as abstracted state machines. This code will be invoked by IEnumerator's methods.

我的黑色迷你裙 2024-08-16 04:54:53

简而言之,(当您等待 marc 的长手版本时)当编译器看到yield 语句时,它会在幕后为您构建一个自定义类的新实例,该实例实现一个名为 IEnumerator 的接口,该实例具有方法 Current()MoveNext(),并跟踪您当前在迭代过程中的位置...在上面的示例中,它将还跟踪要枚举的列表中的值。

in short, (while yr waiting for marc's long-hand version) when the compiler sees yield statements, behind the scenes it builds a new instance of a custom class for you that implements an interface called IEnumerator, which has methods Current(), and MoveNext(), and keeps track of where you are currently in the iteration process... In the case above as your example, it would also keep track of the values in the list to be enumerated through.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文