#c# #iteration #treenode
#c# #итерация #treenode
Вопрос:
У меня есть метод foreach, подобный этому:
TreeNode selectedNode = null;
foreach (TreeNode tn in allNodes)
{
if (selectedNode != null) continue;
var designNodes = tn.Nodes;
foreach (TreeNode item in designNodes)
{
if (item.Tag.ToString().Split('|')[0].Equals(designKey.ToString()))
{
selectedNode = item;
continue;
};
if (selectedNode is null)
{
foreach (TreeNode child in item.Nodes)
{
if (child.Tag != null amp;amp; child.Tag.ToString().Split('|')[0].Equals(designKey.ToString()))
{
selectedNode = child;
continue;
}
if (selectedNode is null)
{
foreach (TreeNode lastLevel in child.Nodes)
{
if (lastLevel.Tag != null amp;amp; lastLevel.Tag.ToString().Split('|')[0].Equals(designKey.ToString()))
{
selectedNode = lastLevel;
continue;
}
}
}
}
}
}
}
Сначала выполняется итерация в родительский элемент TreeNode и присваивается переменная selectedNode
, если она не нашла результатов ( selectedNode
по-прежнему равна нулю), она просматривает дочерние элементы родительского элемента, и если она по-прежнему равна нулю, она просматривает дочерние элементы дочернего элемента.
Код работает, но его слишком сложно прочитать, есть лучший способ реализовать эту итерацию?
Комментарии:
1.
if (selectedNode is null)
должно бытьif (selectedNode == null)
2. Извините, я обновляю свой вопрос @RufusL
3. Не должны ли
continue;
строки бытьbreak;
вместо этого? Похоже, что именно там вы нашли правильное соответствие. Зачем продолжать? Кроме того, цель здесь — найти первый узел, который удовлетворяетTag == designKey
условию? Или что-то еще?4. Да, если он назначает переменную lastDesign, которую я использую, продолжает прерываться, и она должна завершить назначение @RufusL
5.
continue
не прерывается, though…it просто пропускает остальную часть этой итерации и затем продолжает цикл.break
произойдет выход из цикла. Не могли бы вы, пожалуйста, конкретно определить, что должен делать код?
Ответ №1:
Похоже, что то, что вы ищете здесь, — это поиск в ширину, где вы сначала выполняете поиск по всем дочерним элементам, затем по дочерним элементам каждого дочернего элемента и т.д., Пока не найдете совпадение.
Для этого типа поиска мы хотим использовать Queue
, который является «первым входом — первым выходом» (в отличие от стека, который является «последним входом — первым выходом»). Таким образом, мы можем помещать Enqueue
корневые TreeNode
элементы в очередь, затем в цикле мы Dequeue
каждый элемент по одному проверяем, действителен ли он, возвращаем его, если да, и если это не так, то мы Enqueue
считаем его дочерним. Это гарантирует, что мы всегда проверяем родителей перед дочерними элементами.
Я также сделал этот метод немного более гибким, позволив клиенту передавать validator
функцию, которая будет использоваться для проверки TreeNode
. Таким образом, мы можем повторно использовать метод для выполнения поиска в ширину по любым критериям, которые мы пожелаем (мы просто передаем ему метод, который принимает TreeNode
и возвращает bool
, то есть true
, если проверка прошла успешно для этого узла):
private static TreeNode GetFirstMatch(TreeNodeCollection allNodes,
Func<TreeNode, bool> validator)
{
if (allNodes == null) return null;
// Initialize a Queue with all the root nodes
var nodeQueue = new Queue<TreeNode>(allNodes.OfType<TreeNode>());
// Use a queue for a breadth-first search
while (nodeQueue.Any())
{
// Remove the next item
var current = nodeQueue.Dequeue();
// Return it if it passes our validation
if (validator.Invoke(current)) return current;
// Add it's children to the end of the queue
foreach (TreeNode child in current.Nodes)
{
nodeQueue.Enqueue(child);
}
}
// If we didn't find any matches, return null
return null;
}
При использовании мы можем просто передать методу Nodes
свойство нашего TreeView
элемента управления вместе с функцией проверки (в этом примере я передаю лямбда-выражение для функции проверки):
TreeNode firstMatch = GetFirstMatch(treeView1.Nodes,
node => node.Tag.ToString().Split('|')[0].Equals(designKey.ToString()));