Joa*_*nge 10 .net c# ienumerable
基本上我想知道是否可以在代码中多次使用枚举.无论你是否提前破解,枚举总是会在下面的每个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)
{
...
}
Run Code Online (Sandbox Code Playgroud)
编辑: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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 14
消耗序列的代码很好.正如消费者指出的那样,如果树很深,产生枚举的代码可能会出现性能问题.
假设在最深处,你的树有四个深; 想想四个节点上发生了什么.要获取该节点,请迭代调用迭代器的根,该迭代器调用迭代器,该迭代器调用迭代器,迭代器将节点传递回代码,该代码将节点传递回代码,将代码传递回...而不是仅仅将节点交给调用者,你已经制作了一个包含四个人的小斗队,并且他们在最终到达想要它的循环之前,从一个对象到另一个对象的数据流动.
如果树只有四个深,可能没什么大不了的.但假设树是一万个元素,并且有一千个节点在顶部形成链表,而剩下的九千个节点在底部.现在,当您迭代那九千个节点时,每个节点必须通过一千个迭代器,总共九百万个副本才能获取九千个节点.(当然,你可能遇到了堆栈溢出错误并且也崩溃了.)
如果你有这个问题,解决这个问题的方法是自己管理堆栈,而不是在堆栈上推送新的迭代器.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
原始实现的时间复杂度为O(nd),其中n是节点数,d是树的平均深度; 因为d在最坏的情况下可以是O(n),并且在最好的情况下是O(lg n),这意味着算法在时间上在O(n lg n)和O(n ^ 2)之间.它是堆空间中的O(d)(对于所有迭代器)和堆栈空间中的O(d)(对于所有递归调用).
新实现的时间复杂度为O(n),堆空间为O(d),堆栈空间为O(1).
其中一个缺点是订单不同; 在新算法中,树从顶部到底部和从右到左遍历,而不是从上到下,从左到右.如果这困扰你那么你可以说
foreach(var child in current.Effects.Reverse())
Run Code Online (Sandbox Code Playgroud)
代替.
有关此问题的更多分析,请参阅我的同事Wes Dyer关于此主题的文章:
http://blogs.msdn.com/b/wesdyer/archive/2007/03/23/all-about-iterators.aspx
合法,是的.它是否会按预期运行取决于:
IEnumerable返回的执行EffectsRecursive和是否总是返回相同的集合;
是否要同时枚举相同的集合
如果它返回需要一些密集工作的IEnumerable,并且它不会在内部缓存结果,那么您可能需要.ToList()自己进行.如果它确实缓存了结果,那么ToList()会略微多余但可能没有坏处.
此外,如果GetEnumerator()以典型/正确(*)的方式实现,那么您可以安全地枚举任意次数 - 每个都foreach将是一个新的调用GetEnumerator(),返回一个新的实例IEnumerator.但是在某些情况下它可能会返回IEnumerator已经部分或完全枚举的相同实例,因此它实际上只取决于该特定的特定用途IEnumerable.
*我很确定多次返回相同的枚举器实际上违反了模式的隐含合约,但我已经看到了一些实现它的实现.
| 归档时间: |
|
| 查看次数: |
2492 次 |
| 最近记录: |