2009-03-19 4 views
0

У меня есть дерево управления с флажками рядом с каждым узлом, который позволяет проверять, неконтролируемые и средние проверенные состояния на узлах. При нажатии узла родительский элемент и дочерние элементы обновляются. Код, который я нашел, что трюк использует смещение битов, и я пытаюсь понять, что именно происходит.Может кто-нибудь объяснить этот код манипуляции бит?

Может кто-нибудь объяснить следующий код? Или даже лучше, перепишите этот код, чтобы его было легче понять?

// 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); 
    }  
} 

ответ

1

В принципе, такое выражение, как это:

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

Является нелогичным. Обычно вы тестируете биты, сдвигая константу 1 влево, так как это позволяет количество сдвинутых битов быть таким же, как индекс бита, считая бит LSB (самый правый) как бит номер 0.

Также есть нет смысла тестировать результат с помощью сравнения ==, поскольку он всегда будет либо 0, либо отличным от нуля, поэтому вы можете, по крайней мере, проверить что-то более простое, если этого требует ваш язык.

В C и C++, которые по умолчанию интерпретируют ненулевое целое число как «истинное», сравнение совершенно не нужно и просто служит для введения беспорядка, повторения и увеличения риска ошибок.

Я пишу это так:

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

Лишняя скобка должна помочь сделать его более четким, как вещи сгруппированы. Обратите внимание, как "2 < < 1" был переписан как "1 < < 2". Это не просто «переключатель», вам нужно вычислить правильный сдвиг, чтобы получить одно и то же значение бит, в этом случае 4.

Конечно, вы можете поместить бит-тестирование в подпрограммы и вызвать их, чтобы сделать код более читаемым.

+0

Спасибо за ваш ответ. Поэтому, если я правильно понял, я мог бы просто заменить 1 << 2 или 2 << 1 на 4, верно? Есть ли какая-либо польза в использовании сдвига бит вместо постоянного числа 4? –

+0

Без выгоды. Вы также можете заменить (1 << 1 | 2 << 1) на 6. Читабельность, вероятно, такая же плохая, как оригинал, но, по крайней мере, она короче. Жесткое кодирование обоих операндов имеет смысл только при объявлении константы как UNCHECKED = 1 << 2 вместо UNCHECKED = 4, чтобы подчеркнуть, какой бит вы устанавливаете –

2

Похоже, что значение CheckState в элементе управления может быть либо 1, 2, или 4 (или, возможно, 0, 2, и 4?):

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 { 
    ... 
}