Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/298.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可以多次重用IEnumerable集合吗?_C#_.net_Ienumerable - Fatal编程技术网

C# 可以多次重用IEnumerable集合吗?

C# 可以多次重用IEnumerable集合吗?,c#,.net,ienumerable,C#,.net,Ienumerable,基本上,我想知道是否可以在以后的代码中多次使用枚举。无论您是否提前中断,枚举是否会在下面的每个foreach情况下都重置,从而为我们提供从效果集合开始到结束的一致枚举 var effects = this.EffectsRecursive; foreach (Effect effect in effects) { ... } foreach (Effect effect in effects) { if(effect.Name = "Pixelate") break;

基本上,我想知道是否可以在以后的代码中多次使用枚举。无论您是否提前中断,枚举是否会在下面的每个foreach情况下都重置,从而为我们提供从效果集合开始到结束的一致枚举

var effects = this.EffectsRecursive;

foreach (Effect effect in effects)
{
...
}

foreach (Effect effect in effects)
{
    if(effect.Name = "Pixelate")
        break;
}

foreach (Effect effect in effects)
{
...
}
编辑:EffectsRecursive的实现如下:

public IEnumerable<Effect> Effects
{
    get
    {
        for ( int i = 0; i < this.IEffect.NumberOfChildren; ++i )
        {
            IEffect effect = this.IEffect.GetChildEffect ( i );
            if ( effect != null )
                yield return new Effect ( effect );
        }
    }
}

public IEnumerable<Effect> EffectsRecursive
{
    get
    {
        foreach ( Effect effect in this.Effects )
        {
            yield return effect;
            foreach ( Effect seffect in effect.ChildrenRecursive )
                yield return seffect;
        }
    }
}
公共IEnumerable效果
{
得到
{
for(int i=0;i
这一切都取决于
生效类型的实现,是的。它是否能像您期望的那样工作取决于:

  • EffectsRecursive
    返回的
    IEnumerable
    的实现,以及它是否总是返回相同的集合

  • 是否要同时枚举同一集合

如果它返回一个需要大量工作的IEnumerable,并且它没有在内部缓存结果,那么您可能需要
.ToList()
自己完成它。如果它确实缓存了结果,那么
ToList()
将略微冗余,但可能没有什么害处

另外,如果
GetEnumerator()
是以典型/适当的(*)方式实现的,那么您可以安全地枚举任意次数-每个
foreach
都将是对
GetEnumerator()
的新调用,该调用返回
IEnumerator
新实例。但在某些情况下,它可能返回已部分或完全枚举的相同的
IEnumerator
实例,因此这一切实际上取决于该特定
IEnumerable
的特定预期用途


*我很确定多次返回同一个枚举数实际上违反了该模式的隐含约定,但我已经看到一些实现可以这样做。

是的,这样做是合法的。
IEnumerable
模式旨在支持单个源上的多个枚举。只能枚举一次的集合应该公开
IEnumerator

很可能是。IEnumerable的大多数实现返回一个新的IEnumerator,它从列表的开头开始。

使用序列的代码很好。正如spender指出的,如果树很深,则生成枚举的代码可能会有性能问题

假设你的树最深处有四棵树;想想在四个深度的节点上发生了什么。为了得到那个节点,你迭代根,它调用一个迭代器,它调用一个迭代器,它调用一个迭代器,它将节点传递回代码,代码将节点传递回代码,代码将节点传递回。。。不只是将节点交给调用者,而是创建了一个包含四个人的小bucket旅,他们在数据最终到达需要它的循环之前,在对象之间来回移动数据

如果这棵树只有四英尺深,可能没什么大不了的。但是假设树是一万个元素,有一千个节点在顶部形成一个链表,其余的九千个节点在底部。现在,当你迭代这九千个节点时,每个节点都必须通过一千个迭代器,总共九百万个副本才能获取九千个节点。(当然,您可能会遇到堆栈溢出错误,并导致进程崩溃。)

如果您有这个问题,处理这个问题的方法是自己管理堆栈,而不是在堆栈上推新的迭代器

public IEnumerable<Effect> EffectsNotRecursive() 
{     
    var stack = new Stack<Effect>();
    stack.Push(this);
    while(stack.Count != 0)
    {
        var current = stack.Pop();
        yield return current;
        foreach(var child in current.Effects)
            stack.Push(child);
    }
}
相反

有关此问题的更多分析,请参阅我的同事韦斯·戴尔(Wes Dyer)关于此主题的文章:


顺便说一句,我添加了EffectsRecursive的实现。注意递归的产量。在创建状态机的过程中,有很多编译器的神奇之处。如果数据结构较大,则性能可能会比您预期的低/复杂。@spender:谢谢,有没有其他方法可供我使用?@Eric在下面的回答比我的回答更雄辩。@Eric:谢谢Eric,这是一个惊人的回答。我将像这样更改递归枚举。但在你的例子的最后,你的例子,它将从9000个项目中最顶端的父项开始,然后从上到下迭代9000个项目,然后从我们作为第一个元素迭代的第一个最顶端的父项开始,移动到其他最顶端的父项,直到我们以最左边的元素结束?这就是你说的“树从上到下,从右到左遍历”的意思吗?@Eric:Btw你说的“stack.Push(this);”也返回当前实例,对吗?在我的实现中,我只想返回当前效果的子效果,而不返回其本身。我能把它拿走吗?我觉得这会导致stack.Pop()行出现问题。你觉得怎么样?@Joan:这是你的第一个问题:假设树是A,左子B,右子C。这个算法按A,C,B的顺序生成。首先我们把A放在堆栈上。然后我们弹出A,把B和C放在堆栈上。然后我们弹出C,然后弹出B。B和C是从右到左,而不是从左到右。如果需要A、B、C,则在推送子列表之前反转它们。推A,弹出A,推C,推B,弹出B,弹出C,现在我们有了A,B,C。@Joan:这是你的第二个问题:你能做的是在进入循环之前把“这个”的子项推到堆栈上,让球滚动。@Joan:你忘了paren
        foreach(var child in current.Effects.Reverse())