2014-03-06 117 views
0

枚举器如何在AS3中实现?我想循环使用for each循环的对象,以便遍历树或显示列表中的所有对象。如何在AS3中实现枚举器?

我会想象Proxy类可以派上用场,它的nextName,nextNameIndex,nextValue和getProperty方法。

+1

你在最后一句中回答了你自己的问题。 – Marty

+0

这不是一个答案,这是一个提示。而且我经常发布问题,我已经知道答案并准备发布我已有的实施方案,只是作为对社区的贡献。我也有兴趣看到别人的实现和可能的解决方案,我可能没有想到。 – Triynko

回答

1

这是我实现的显示列表枚举,这将遍历DisplayObjectContainer的孩子们在深度优先庄园:由于枚举扩展Proxy类,并实现其列举相关的方法

package flos.utils 
{ 
    import flash.display.DisplayObject; 
    import flash.display.DisplayObjectContainer; 
    import flash.utils.flash_proxy; 
    import flash.utils.Proxy; 
    import flos.system.IDisposable; 

    public class DisplayListEnumerator extends Proxy implements IDisposable 
    { 
     private var list:Vector.<DisplayObject>; 
     private var root:DisplayObjectContainer; 

     public function DisplayListEnumerator(root:DisplayObjectContainer) 
     { 
      this.root = root; 
     } 

     public function dispose():void 
     { 
      root = null; 
     } 

     private function get disposed():Boolean 
     { 
      return root == null; 
     } 

     private function enumDisplayList():void 
     { 
      var _childIndex:int = 0; 
      var _childIndexStack:Vector.<int> = new Vector.<int>(); 
      var _currentContainer:DisplayObjectContainer = root; 
      var _current:DisplayObject; 
      list = new Vector.<DisplayObject>(); 
      enumerate: do 
      { 
       for (var i:int = _childIndex; i < _currentContainer.numChildren; i++) 
       { 
        _current = _currentContainer.getChildAt(i); 
        list.push(_current); 
        if (_current is DisplayObjectContainer) 
        { 
         _childIndexStack.push(i + 1); 
         _childIndex = 0; 
         _currentContainer = _current as DisplayObjectContainer; 
         continue enumerate; 
        } 
       } 
       if (_currentContainer == root) 
        break enumerate; //break when we finish enumerating the root element 
       else 
       { 
        _childIndex = _childIndexStack.pop(); 
        _currentContainer = _currentContainer.parent; 
       } 
      } while (true); 
     } 

     override flash_proxy function nextNameIndex(index:int):int 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      if (index == 0) //initial call, cache the current display list in a vector 
      { 
       enumDisplayList(); 
       return 1; 
      } 
      else if (index < list.length) 
       return index + 1; 
      else 
       return 0; 
     } 

     override flash_proxy function nextName(index:int):String 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      var i:int = index - 1; 
      if (i > -1 && i < list.length) 
       return String(i); 
      else 
       return null; 
     } 

     override flash_proxy function nextValue(index:int):* 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      var i:int = index - 1; 
      if (i > -1 && i < list.length) 
       return list[i]; 
      else 
       return undefined; 
     } 
    } 
} 

,它可以被用在每个循环,像这样:

import flos.utils.DisplayListEnumerator; 
import flash.display.DisplayObject; 
import flash.display.DisplayObjectContainer; 

var i:int = 0; 
for each (var d:DisplayObject in (new DisplayListEnumerator(root as DisplayObjectContainer))) 
    trace((i++) + " " + d.name); 

当然,这大概可以不预索引显示列表来实现,但这需要额外的检查,如确保在索引接下来的电话NameIndex不是乱序调用的(并且实际上是从调用调用中增加的),以非递归方式维护一个枚举在每个级别的堆栈,就像我在上面的实现中那样不会将显示列表编入索引的解决方案,如果在枚举过程中显示列表完全被更改,则会出现错误**甚至无限循环。我在这里提供的实现在枚举生命中获取显示列表的快照可能是最稳定的。

**我自己也实现了非预索引版本,所以我可以告诉你它们实际上并不稳定,特别是在你进行任何类型的调试时,因为调试器会试图枚举枚举器的属性当你进入代码时,立即破坏活动枚举状态,所以当枚举开始时,我上面提供的实现(特别是一种预先缓存显示列表的实现类型)实际上可能是唯一的稳定实现这一点的方式。

这是非预索引版本,它不是在枚举生成时将整个显示列表编入索引,而是在每个循环的每次迭代中逐个遍历显示层次节点。这种类型的实现经历了从调试器的“本土派”,然而窗口,它试图“枚举枚举”(某事的Visual Studio确实会警告你)的干扰,所以我建议不要使用下列类型的实现:

package flos.utils 
{ 
    import flash.display.DisplayObject; 
    import flash.display.DisplayObjectContainer; 
    import flash.utils.flash_proxy; 
    import flash.utils.Proxy; 
    import flos.system.IDisposable; 

    public class DisplayListEnumeratorRealtime extends Proxy implements IDisposable 
    { 
     private var list:Vector.<DisplayObject>; 
     private var root:DisplayObjectContainer; 
     private var _enumIndex:int; 
     private var _parentStack:Vector.<DisplayObjectContainer>; 
     private var _childIndexStack:Vector.<int>; 
     private var _childIndex:int; 
     private var _current:DisplayObject; 
     private var _currentContainer:DisplayObjectContainer; 

     public function DisplayListEnumeratorRealtime(root:DisplayObjectContainer) 
     { 
      this.root = root; 
     } 

     public function dispose():void 
     { 
      root = null; 
     } 

     private function get disposed():Boolean 
     { 
      return root == null; 
     } 

     private function reset():void 
     { 
      _childIndexStack = new Vector.<int>(); 
      _parentStack = new Vector.<DisplayObjectContainer>(); 
      _currentContainer = root; 
      _current = null; 
      _enumIndex = 0; 
      _childIndex = 0; 
     } 

     private function pushChild():void 
     { 
      _parentStack.push(_currentContainer); 
      _childIndexStack.push(_childIndex + 1); 
      _childIndex = 0; 
      _currentContainer = _current as DisplayObjectContainer; 
     } 

     private function popChild():void 
     { 
      _childIndex = _childIndexStack.pop(); 
      _currentContainer = _parentStack.pop(); 
     } 

     override flash_proxy function nextNameIndex(index:int):int 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      if (index == 0) //initial call, cache the current display list in a vector 
       reset(); 

      if (index == _enumIndex) //ensure expected index is passed 
      { 
       _enumIndex = index + 1; 
       processNextChild: do 
       { 
        if (_childIndex < _currentContainer.numChildren) 
        { 
         _current = _currentContainer.getChildAt(_childIndex); 
         if (_current is DisplayObjectContainer) 
         { 
          pushChild(); 
          return _enumIndex; 
         } 
         _childIndex++; 
         return _enumIndex; 
        } 
        else if (_parentStack.length > 0) 
        { 
         popChild(); 
         continue processNextChild; 
        } 
        break; 
       } while (true); 
       if (_currentContainer == root) 
        return 0; //enumeration is complete when enumeration of the root element is complete 
       return _enumIndex; 
      } 
      else 
      { 
       throw new Error("Enumeration index called out of order."); 
      } 
     } 

     override flash_proxy function nextName(index:int):String 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      if (index == _enumIndex) 
       return String(_enumIndex); 
      else 
       throw new Error("Enumeration index called out of order."); 
     } 

     override flash_proxy function nextValue(index:int):* 
     { 
      if (disposed) 
       throw new Error("Enumerator disposed."); 
      if (index == _enumIndex) 
       return _current; 
      else 
       throw new Error("Enumeration index called out of order."); 
     } 
    } 
}