代码之家  ›  专栏  ›  技术社区  ›  Christophe Herreman

有人能解释一下这个位操作代码吗?

  •  0
  • Christophe Herreman  · 技术社区  · 17 年前

    我有一个树控件,每个节点旁边都有复选框,允许节点上的选中、未选中和中间选中状态。单击节点时,将更新父节点和子节点。我发现的代码使用了位移位,我试图理解到底发生了什么。

    有人能解释一下下面的代码吗?或者更好,重写这段代码以便更容易理解?

    // click event handler
    private function eventMessageTree_itemCheckHandler(event:TreeEvent):void {
      var node:ITreeNode = ITreeNode(event.item);
      var checkState:uint = TreecheckboxItemRenderer(event.itemRenderer).checkBox.checkState;
      updateParents(node, checkState);
      updateChilds(node, checkState);
    }
    
    private function updateChilds(item:ITreeNode, value:uint):void {
      var middle:Boolean = (value & 2 << 1) == (2 << 1);
      var selected:Boolean = (value & 1 << 1) == (1 << 1);
    
      if (item.children.length > 0 && !middle) {
        for each (var childNode:ITreeNode in item.children)     {
          childNode.checked = value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0";
          updateChilds(childNode, value);
        }
      }
    }
    
    private function updateParents(item:ITreeNode, value:uint): void {
      var checkValue:String = (value == (1 << 1 | 2 << 1) ? "2" : value == (1 << 1) ? "1" : "0");
      var parentNode:ITreeNode = item.parent;
      if (parentNode) {
        for each (var childNode:ITreeNode in parentNode.children) {
          if (childNode.checked != checkValue) {
            checkValue = "2";
          }
        }
        parentNode.checked = checkValue;
        updateParents(parentNode, value);
      }    
    }
    
    2 回复  |  直到 17 年前
        1
  •  2
  •   Michael Brewer-Davis    17 年前

    public static const CONTROL_UNCHECKED:uint = 1; // not checked, and some descendants are
    public static const CONTROL_CHECKED:uint = 2; // checked, and all descendants are
    public static const CONTROL_MIDDLE:uint = 4; // not checked, but some descendants are
    

    而节点中的选中值可以是0、1或2:

    public static const UNCHECKED:uint = 0; // not checked, and some descendants are
    public static const CHECKED:uint = 1; // checked, and all descendants are
    public static const MIDDLE:uint = 2; // not checked, but some descendants are
    

    这真让人困惑。理想情况下,这些将是相同的常数集。

    要更新:

    private function controlStateToNodeState(value:uint):uint {
       return value / 2;
    }
       ...
       updateParents(node, controlStateToNodeState(checkState));
       updateChilds(node, controlStateToNodeState(checkState));
       ...
    
    /** Updates the descendants of the node based on state:
     *  If value is CHECKED, all children are CHECKED
     *  If value is UNCHECKED, all children are UNCHECKED
     *  If value is MIDDLE, children are left alone
     */
    private function updateChilds(item:ITreeNode, value:uint):void {
       if (value == MIDDLE) {
          return;  // if value is MIDDLE, children are left alone
       }
    
       // not middle, so update all children to my state
       for each (var childNode:ITreeNode in item.children)     {
          childNode.checked = value;
          updateChilds(childNode, value);
        }
      }
    }
    
    /**
     * Updates the ancestor nodes based on state:
     * If value is CHECKED, ancestors are made MIDDLE if not already checked
     * If value is MIDDLE, ancestors are made middle (they should not already be CHECKED)
     */
    private function updateParents(item:ITreeNode, value:uint): void {
        ...
    }
    
        2
  •  1
  •   unwind    17 年前

    基本上,这样的表达:

    var middle:Boolean = (value & 2 << 1) == (2 << 1);
    

    这是违反直觉的。您通常通过将常数1向左移位来测试位,因为这样可以使移位的位数与位的索引相同,将LSB(最右边的)位计算为位号0。

    此外,使用==比较测试结果也没有意义,因为它总是为0或非零,所以如果您的语言需要,您至少可以测试一些更简单的东西。

    在C和C++中,默认情况下,将非零整数解释为“true”,比较完全是多余的,只是用来引入杂波、重复和增加bug的风险。

    我会这样写:

    var middle:Boolean = (value & (1 << 2)) != 0;
    

    额外的括号应该有助于更清楚地说明事物是如何分组的。请注意“2<<1”是如何改写为“1<<2”的。这不仅仅是一个“开关”,您需要计算适当的移位以获得相同的位值,在这种情况下为4。

    当然,您可以将位测试放入子例程并调用它们,以使代码更具可读性。

    推荐文章