在 C# 中生成

Posted

技术标签:

【中文标题】在 C# 中生成【英文标题】:Yield in C# 【发布时间】:2009-06-10 21:42:58 【问题描述】:

这在 c 中有什么等价物吗?

【问题讨论】:

为什么需要yield?如果你有一个数组,你可以遍历它。 Yield 与 IEnumerables 相关,您通常会在“foreach”情况下使用它。既然你不能'foreach',为什么你需要创建一个'yield'? 【参考方案1】:

Yield 由编译器实现为实现状态机的自定义类。虽然您无法轻松获得语法(除非您使用之前指定的 Fiber 方法),但您可以非常简单地自己复制结果,尽管这很乏味。下面是方法(我将在 C# 中展示,您必须根据您使用的类型在 C++ 中做适当的事情):

假设如下代码:

public IEnumerable<T> GetOddStrings(
  IEnumerable<IEnumerable<string>> stringCollections)

    foreach(var stringCollection in stringCollections)
      foreach(var str in stringCollection)
      
        if(str.Length %2 != 0) yield return str;
        if(str.Length == 42) yield break;  // 42 is BAD! Stop immediately
      

1) 将所有 foreach 方法展开为显式枚举器调用:

public IEnumerable<T> GetOddStrings(
  IEnumerable<IEnumerable<string>> stringCollections)

  var firstEnumerator = stringCollection.GetEnumerator();
  while(firstEnumerator.MoveNext())
  
    var secondEnumerator = firstEnumerator.Current.GetEnumerator();
    while(secondEnumerator.MoveNext())
     
      var str= secondEnumerator.Current;
      if(str.Length %2 != 0) yield return str;
      if(str.Length == 42) yield break;
    
  

2) 将所有局部变量移动到方法的顶部:

public IEnumerable<T> GetOddStrings(
  IEnumerable<IEnumerable<string>> stringCollections)

  IEnumerator<IEnumerable<string>> firstEnumerator;
  IEnumerator<string> secondEnumerator;
  string str;

  firstEnumerator = stringCollections.GetEnumerator();
  while(firstEnumerator.MoveNext())
  
    secondEnumerator = firstEnumerator.Current.GetEnumerator();
    while(secondEnumerator.MoveNext())
     
      str= secondEnumerator.Current;
      if(str.Length %2 != 0) yield return str;
      if(str.Length == 42) yield break;
    
  

3) 移动到带有嵌套 switch 语句的循环结构。 a) 更改状态并为每个收益返回继续循环。 b) 反转 if 条件 c) 每个退出条件的屈服中断(下面我们反转 if)。

public IEnumerable<T> GetOddStrings(
  IEnumerable<IEnumerable<string>> stringCollections)

  IEnumerator<IEnumerable<string>> firstEnumerator;
  IEnumerator<string> secondEnumerator;
  string str;
  int state = 0;

  while(true)
  
    switch(state)
    
      case 0:
        firstEnumerator = stringCollections.GetEnumerator();
        // This could be "yield break" but I want to show how you
        // could split ifs with significant code in the else
        if(!firstEnumerator.MoveNext()) 
         
          state = 1; 
          continue; 
        

        secondEnumerator = firstEnumerator.Current;
        if(!secondEnumerator.MoveNext) continue;
        state = 2;
        if(str.Length %2 != 0) yield return str;
        continue;

      case 1:
        yield break;

      case 2:
        if(str.Length == 42) yield break;
        state = 0;
        continue;
    
  

4) 进入一个类并从您的方法中返回该类: a)yield break 变成“return false”; b) 收益回报变成“this.Current = ??; return true;”

public IEnumerable<T> GetOddStrings(
  IEnumerable<IEnumerable<string>> stringCollections)

  return new OddStringEnumerable(stringCollections);


private class OddStringEnumerable : IEnumerable<string>

  IEnumerable<IEnumerable<string>> stringCollections;
  IEnumerator<IEnumerable<string>> firstEnumerator;
  IEnumerator<string> secondEnumerator;
  string str;
  int state;

  public OddStringEnumerable(IEnumerable<IEnumerable<string>> stringCollections)
  
    this.stringCollections = stringCollections;
  

  public string Current  get; private set; 

  public bool MoveNext()
  
    while(true)
    
      switch(state)
      
        case 0:
          firstEnumerator = this.stringCollections.GetEnumerator();
          if(!this.firstEnumerator.MoveNext()) 
           
            this.state = 1; 
            continue; 
          

          this.secondEnumerator = this.firstEnumerator.Current;
          if(!secondEnumerator.MoveNext) continue;

          this.state = 2;
          if(str.Length %2 != 0) 
          
            this.Current = str;
            return true;
          
          continue;

        case 1:
          return false;

        case 2:
          if(str.Length == 42) return false;
          this.state = 0;
          continue;
      
    
  

5) 酌情优化。

【讨论】:

【参考方案2】:

纤维?哦,这个:

使用 Fibers 的原生 C++ 的收益返回迭代器http://www.codeproject.com/KB/library/fiber-based_iterator.aspx

【讨论】:

【参考方案3】:

虽然 C 在枚举集合方面没有与 yield 相同的概念,但它确实具有创建协程和纤维的能力。

这里有一些可能感兴趣的***文章:

http://en.wikipedia.org/wiki/Setcontext http://en.wikipedia.org/wiki/Setjmp/longjmp

【讨论】:

我认为这太复杂了。最好的方法是只创建一个包含 yieldindex 变量和数组的结构,然后每次“yield”时增加索引。这样你的'yield'只是从yieldindex点中提取数组值。 我并不是说它不复杂,只是那是 C 的等价物。有很多方法可以解决这个问题...这就是我们编写软件的原因 =)【参考方案4】:

Coroutines in C 使用了一些预处理程序,但实现了一个相当自然的(相对于 C 中的任何其他内容)yield

而你会用 Python 写这个:

"""This is actually a built-in function.
def range(start, stop, step):
    i = start
    while i < stop:
        yield i
        i = i + step
"""

if __name__ == '__main__':
    import sys
    start = int(sys.argv[1]) if len(sys.argv) > 2 else 0
    stop = int(sys.argv[2]) if len(sys.argv) > 2 else int(sys.argv[1])
    step = int(sys.argv[3]) if len(sys.argv) > 3 else 1
    for i in range(start, stop, step):
        print i,
    print

coroutine.h 让您可以用 C 语言编写:

#include <coroutine.h>
#include <stdio.h>

int range(int start, int stop, int step) 
    static int i;

    scrBegin;
    for (i = start; i < stop; i += step)
        scrReturn(i);
    scrFinish(start - 1);


int main(int argc, char **argv) 
    int start, stop, step, i;

    start = argc > 2 ? atoi(argv[1]) : 0;
    stop = argc > 2 ? atoi(argv[2]) : atoi(argv[1]);
    step = argc > 3 ? atoi(argv[3]) : 1;

    while ((i = range(start, stop, step)) >= start)
        printf("%d ", i);
    printf("\n");

$ cc 范围.c $ ./a.out 10 0 1 2 3 4 5 6 7 8 9

对于更复杂且需要重入的东西,Python 中的Hamming numbers:

def hamming():
    yield 1

    i2 = (2*x for x in hamming())
    i3 = (3*x for x in hamming())
    i5 = (5*x for x in hamming())

    m2, m3, m5 = i2.next(), i3.next(), i5.next()

    while True:
        if m2 < m3:
            if m2 < m5:
                yield m2
                m2 = i2.next()
            else:
                if m2 > m5: yield m5
                m5 = i5.next()
        elif m2 == m3: m3 = i3.next()
        elif m3 < m5:
            yield m3
            m3 = i3.next()
        else:
            if m3 > m5: yield m5
            m5 = i5.next()

if __name__ == '__main__':
    import sys
    it = hamming()
    for i in range(str(sys.argv[1]) if len(sys.argv) > 1 else 25):
        print it.next(),
    print

和C:

#include <coroutine.h>
#include <stdio.h>

int hamming(ccrContParam) 
    ccrBeginContext;
    ccrContext z[3];
    int m2, m3, m5;
    ccrEndContext(state);

    ccrBegin(state);
    state->z[0] = state->z[1] = state->z[2] = 0;
    ccrReturn(1);

#define m2_next (2*hamming(&state->z[0]))
#define m3_next (3*hamming(&state->z[1]))
#define m5_next (5*hamming(&state->z[2]))

    state->m2 = m2_next, state->m3 = m3_next, state->m5 = m5_next;

    while (1) 
        if (state->m2 < state->m3) 
            if (state->m2 < state->m5) 
                ccrReturn(state->m2);
                state->m2 = m2_next;
             else 
                if (state->m2 > state->m5) ccrReturn(state->m5);
                state->m5 = m5_next;
            
         else if (state->m2 == state->m3) state->m3 = m3_next;
        else if (state->m3 < state->m5) 
            ccrReturn(state->m3);
            state->m3 = m3_next;
         else 
            if (state->m3 > state->m5) ccrReturn(state->m5);
            state->m5 = m5_next;
        
    
    ccrFinish(-1);


int main(int argc, char **argv) 
    int count = argc > 1 ? atoi(argv[1]) : 25, i;
    ccrContext z = 0;

    for (i = 0; i < count; i++)
        printf("%d ", hamming(&z));
    printf("\n");

$ cc hamming.c $ ./a.out 1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 40 45 48 50 54

【讨论】:

【参考方案5】:

没有。不过,在 Windows 上,您可以使用纤维来实现类似的效果。

【讨论】:

【参考方案6】:

没有。但是,您可以使用setjmp, longjmp 在 C 中实现类似的效果,但这非常很棘手。

【讨论】:

【参考方案7】:

在 C# 中,yield 简化了为集合创建 IEnumberables。

在 C++ 中,您必须使用 STL 迭代器。

【讨论】:

以上是关于在 C# 中生成的主要内容,如果未能解决你的问题,请参考以下文章

使用 JWT 在 c# 中生成令牌的无效签名

在 C# 中生成随机数 [重复]

尝试在 C# 中生成按钮

为啥在 C# 中生成的代码使用下划线? [关闭]

使用 XSD 文件在 C# 中生成 XML 文件

使用 ASP.net、C#、MVC 在模板中生成 pdf