C# 由IEnumerable生成的消费序列

C# 由IEnumerable生成的消费序列,c#,yield-return,C#,Yield Return,我想使用IEnumerable生成一个值序列——具体来说,是一个类似Excel的列标题列表 private IEnumerable<string> EnumerateSymbolNames() { foreach (var sym in _symbols) { yield return sym; } foreach (var sym1 in _symbols) { foreach (var sym2 in _symbols) {

我想使用
IEnumerable
生成一个值序列——具体来说,是一个类似Excel的列标题列表

private IEnumerable<string> EnumerateSymbolNames()
{
  foreach (var sym in _symbols)
  {
    yield return sym;
  }

  foreach (var sym1 in _symbols)
  {
    foreach (var sym2 in _symbols)
    {
      yield return sym1 + sym2;
    }
  }

  yield break;
}
private readonly string[] _symbols = new string[] { "A", "B", "C", ...};
。。。但这些都不起作用。(所有重复返回“A”。) 根据对的回复,我想知道我想要什么甚至是可能的——尽管对那篇文章的一些回复建议使用与我上一篇类似的技巧


不,这不是家庭作业:)

当您使用
GetEnumerator
时,每次迭代都需要使用相同的枚举器。如果第二次调用GetEnumerator,它将在集合的开头重新开始


如果要使用
Take
,必须首先跳过已处理的记录数。

使用
GetEnumerator
时,每次迭代都需要使用相同的枚举器。如果第二次调用GetEnumerator,它将在集合的开头重新开始


如果要使用
Take
,必须首先跳过已处理的记录数。

此代码对我有效

var states = EnumerateSymbolNames();
var stateMachine = states.GetEnumerator();

do
{
  //something
} while (stateMachine.MoveNext());
在该循环中打印结果时,它成功地生成以下输出:

A
B
C
...
AA
AB
AC
...
BA
BB
...

这正是我认为你想要的…

这段代码对我有用

var states = EnumerateSymbolNames();
var stateMachine = states.GetEnumerator();

do
{
  //something
} while (stateMachine.MoveNext());
在该循环中打印结果时,它成功地生成以下输出:

A
B
C
...
AA
AB
AC
...
BA
BB
...

这正是我认为您想要的…

正如@cadrell0的回答和@Mr Steak的评论所指出的,我需要做的是保留对
EnumerateSymbolNames().GetEnumerator()返回的枚举器的引用

当您在
foreach
循环中时,这是为您隐式完成的:迭代器变量包装一个枚举器,它(我假设)的作用域是循环的局部。所以——这是关键——当迭代器块执行时(相当于)

。。。您总是使用相同的枚举数。而我所做的是每次创建/获取一个不同的(新)枚举器。可以预见的是,它总是从序列的第一个位置开始。这可能对每个人都很明显,但我除外;事后看来,这对我来说也是显而易见的。(我认为枚举数是序列的一种单例属性,假设每次都返回相同的枚举数。)

以下是我想要的:

public class SymbolGenerator
{
    private readonly string[] _symbols = { "A", "B", "C", ... };

    private readonly IEnumerator<string> _enumerator;

    public SymbolGenerator()
    {
        _enumerator = EnumerateSymbols().GetEnumerator();
    }

    public string GetNextSymbol()
    {
        _enumerator.MoveNext();
        return _enumerator.Current;
    }

    private IEnumerable<string> EnumerateSymbols()
    {
        // (unchanged)
    }
}
公共类符号生成器
{
私有只读字符串[]_符号={“A”、“B”、“C”、…};
私有只读IEnumerator\u枚举器;
公共符号生成器()
{
_枚举器=枚举符号().GetEnumerator();
}
公共字符串GetNextSymbol()
{
_枚举数。MoveNext();
返回_enumerator.Current;
}
私有IEnumerable枚举符号()
{
//(不变)
}
}

正如@cadrell0的回答和@Mr Steak的评论所指出的,我需要做的是保留对
EnumerateSymbolNames().GetEnumerator()返回的枚举数的引用。

当您在
foreach
循环中时,这是为您隐式完成的:迭代器变量包装一个枚举器,它(我假设)的作用域是循环的局部。所以——这是关键——当迭代器块执行时(相当于)

。。。您总是使用相同的枚举数。而我所做的是每次创建/获取一个不同的(新)枚举器。可以预见的是,它总是从序列的第一个位置开始。这可能对每个人都很明显,但我除外;事后看来,这对我来说也是显而易见的。(我认为枚举数是序列的一种单例属性,假设每次都返回相同的枚举数。)

以下是我想要的:

public class SymbolGenerator
{
    private readonly string[] _symbols = { "A", "B", "C", ... };

    private readonly IEnumerator<string> _enumerator;

    public SymbolGenerator()
    {
        _enumerator = EnumerateSymbols().GetEnumerator();
    }

    public string GetNextSymbol()
    {
        _enumerator.MoveNext();
        return _enumerator.Current;
    }

    private IEnumerable<string> EnumerateSymbols()
    {
        // (unchanged)
    }
}
公共类符号生成器
{
私有只读字符串[]_符号={“A”、“B”、“C”、…};
私有只读IEnumerator\u枚举器;
公共符号生成器()
{
_枚举器=枚举符号().GetEnumerator();
}
公共字符串GetNextSymbol()
{
_枚举数。MoveNext();
返回_enumerator.Current;
}
私有IEnumerable枚举符号()
{
//(不变)
}
}

在上一个示例中,只需将
枚举器
设置为字段并初始化一次,而不是每次调用
EnumerateSymbolNames().GetEnumerator()
即可。在上一个示例中,只需将
枚举器
设置为字段并初始化一次,而不是调用
EnumerateSymbolNames().GetEnumerator()
每次。不是为了反驳这个答案中的任何内容,而是为了补充一点,枚举器就是问题中提到的状态机。enumerable在初始状态下生成该状态机的一个新实例。不反驳此答案中的任何内容,但要补充一点,枚举器就是问题中提到的状态机。可枚举项在初始状态下生成该状态机的新实例。