2016-10-28 2 views
0

У меня есть метод рекурсии, который установит идентификатор узла, используя какой-то индекс, я царапаю свою голову, так как он продолжает жаловаться, что у меня есть дубликаты идентификаторов.C# Recursion Duplicate Id

Я уверен, что целочисленный тип передается по значению, поэтому я не уверен, что здесь не так.

IList<DocumentNode> contents = new List<DocumentNode>(); 
int index = 0; 
LoadContents(null, index++, result, contents); 

private void LoadContents(DocumentNodeparentNode, int nodeId, WordContent wordContent, IList<DocumentNode> contents) 
{  
    foreach (OtheClassContent childContent in wordContent.ChildrenContent) 
    { 
     var node = new DocumentNodeViewModel(nodeId, 
      parentNode?.NodeId, 
      childContent.SortOrder, 
      childContent.Depth); 

     contents.Add(node); 
     nodeId++; 
     LoadContents(node, nodeId, childContent, contents); 
    } 
} 

// This line wont work 
IDictionary<int, int?> myDictionary = contents.ToDictionary(a => a.Id, a => a.ParentId); 

Узел документа является структурой узла простого дерева

public class DocumentNode 
{ 
    public DocumentNode(
     int nodeId, 
     int? parentNodeId, 
     short sortOrder, 
     short branchLevel) 
    { 
     NodeId = nodeId; 

     SortOrder = sortOrder; 
     BranchLevel = branchLevel; 
     ParentNode = null; 
     ParentNodeId = parentNodeId; 
     ChildNodes = Enumerable.Empty<DocumentNode>(); 
    } 

    public int NodeId { get; private set; } 
    public int? ParentNodeId { get; private set; } 
    public short SortOrder { get; private set; } 
    public DocumentNode ParentNode { get; set; } 
    public IEnumerable<DocumentNode> ChildNodes { get; set; } 
    public short BranchLevel { get; private set; } 
} 

Кто-нибудь знает, что я делаю неправильно?

+0

Пример: вы создаете узел с 'nodeID' = 3 и увеличиваете до 4. Затем повторяется ive вызов создает узлы 4,5,6. Но обратно в исходный 'nodeID' все еще 4. Петля продолжается, и вы создаете узел 4, но он уже создан. Проблема решена, если вы передаете 'nodeID'' ref'. –

ответ

1

Вы прохождения nodeId по значению, так вот что происходит:

Child1: nodeId = 0 
    Grandchild1_1: nodeId = 1 
    Grandchild1_2: nodeId = 2 
Child2: nodeId = 1 
    Grandchild2_1: nodeId = 2 
    Grandchild2_2: nodeId = 3 
... 

Если передать его по ссылке, все рекуррентные вызовы могут увеличить то же значение, производя это:

Child1: nodeId = 0 
    Grandchild1_1: nodeId = 1 
    Grandchild1_2: nodeId = 2 
Child2: nodeId = 3 
    Grandchild2_1: nodeId = 4 
    Grandchild2_2: nodeId = 5 
... 

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

Альтернатива использованию ref возвращает последнее количество узлов из функции. Итак, вот упрощенная идея ваших двух альтернатив.

Ссылки:

private void RecursiveCallWithRefs (Node current, ref int nodeId) 
{ 
    foreach (var child in current.Children) 
    { 
     child.FillOutContents(nodeId); 
     ++nodeId; 
     RecursiveCallWithRefs(child, ref nodeId); 
    } 
} 

Возвращаемые значения:

private int RecursiveCallWithReturns (Node current, int nodeId) 
{ 
    foreach (var child in current.Children) 
    { 
     child.FillOutContents(nodeId); 
     nodeId = RecursiveCallWithReturns(child, nodeId + 1); 
    } 

    return nodeId; 
} 

Конечно, если бы я тебя, я бы обернуть это в более абстрактном вызова:

private void FillOutNodesWithRefs (Node startingNode) 
{ 
    int startingId = 0; 
    RecursiveCallWithRefs(startingNode, ref startingId); 
} 

private void FillOutNodesWithReturns (Node startingNode) 
{ 
    RecursiveCallWithReturns(startingNode, 0); 
} 
1

Ваша проблема в том, что целое число передается по значению. Кажется, что при добавлении узла в ваш цикл foreach вы добавляете узел, а затем отправляете инкрементный id для рекурсии, а тот же идентификатор используется главным вызовом, а также для рекурсивного вызова.

Скажем, например, у вас есть 2 элемента в wordContent.ChildrenContent при первом вызове, он создает первый узел с id = 0 и увеличивает его до 1, а затем вызывает рекурсию с id = 1. Предположим, что у вас есть 2 элемента в словеContent.ChildrenContent в вызове рекурсии тоже, в этом случае рекурсия добавляет новые узлы с идентификаторами 1 и 2, а элемент управления возвращается к основному методу. Здесь id все еще 1, и теперь создается еще один узел с идентификатором 1.

Я думаю, что все будет хорошо, если вы передадите идентификатор со ссылкой.

IList<DocumentNode> contents = new List<DocumentNode>(); 
int index = 0; 
LoadContents(null, ref index++, result, contents); 

private void LoadContents(DocumentNode parentNode, ref int nodeId, WordContent wordContent, IList<DocumentNode> contents) 
{  
    foreach (OtheClassContent childContent in wordContent.ChildrenContent) 
    { 
     var node = new DocumentNodeViewModel(nodeId, 
      parentNode?.NodeId, 
      childContent.SortOrder, 
      childContent.Depth); 

     contents.Add(node); 
     nodeId++; 
     LoadContents(node, ref nodeId, childContent, contents); 
    } 
} 

// This line wont work 
IDictionary<int, int?> myDictionary = contents.ToDictionary(a => a.Id, a => a.ParentId);