Удаление элементов из списков и всех ссылок на них - PullRequest
7 голосов
/ 28 апреля 2010

Я сталкиваюсь с ситуацией, когда у меня есть зависимые объекты, и я хотел бы иметь возможность удалить объект и все ссылки на него.

Скажем, у меня есть объектная структура, подобная приведенному ниже, с типом Branch, который ссылается на два узла.

public class Node
{
    // Has Some Data!
}

public class Branch
{
    // Contains references to Nodes
    public Node NodeA
    public Node NodeB
}

public class Graph
{
    public List<Node> Nodes;
    public List<Branch> Branches;
}

Если я удаляю Node из списка Nodes в классе Graph, все еще возможно, что один или несколько объектов Branch по-прежнему содержат ссылку на удаленный Node, таким образом, сохраняя его в памяти, тогда как на самом деле, что бы я хотел быть для установки любых ссылок на удаленный узел равным нулю и позволить запускать сборку мусора.

Кроме перечисления через каждую Ветвь и последовательной проверки каждой ссылки на Узел, есть ли какие-нибудь умные идеи о том, как удалить ссылки на Узел в каждом экземпляре Ветви И, действительно, любой другой класс, который ссылается на удаленный Узел?

Ответы [ 6 ]

6 голосов
/ 28 апреля 2010

Нет встроенной функции языка C #, чтобы облегчить это (вы не можете реально отслеживать задания). Вам нужно будет где-то отслеживать все ссылки и обновлять их, как только вы назначаете для них новую ссылку. Очень общая идея состоит в том, чтобы обеспечить событие Removed в самом Node и вызвать событие, когда объект должен быть оставлен. Каждый раз, когда вы хотите сохранить новую ссылку на Node, вы подписываетесь на событие с соответствующим делегатом, который обнуляет ссылку на этот объект. Конечно, если вы делаете это с набором ранее известных типов, которые ссылаются на узел определенным образом, могут быть более простые и эффективные способы выполнения задачи.

1 голос
/ 28 апреля 2010

Измените свой узел, включив в него список филиалов, в которых он находится:

public class Node
{
    // Has Some Data!

    public List<Branch> BranchesIn;
    public List<Branch> BranchesOut;  // assuming this is a directed graph

    public void Delete()
    {
      foreach (var branch in BranchesIn)
        branch.NodeB.BranchesOut.Remove(branch);

      foreach (var branch in BranchesOut)
        branch.NodeA.BranchesIn.Remove(branch);

      BranchesIn.Clear();
      BranchesOut.Clear();
     }
}

public class Branch
{
    // Contains references to Nodes
    public Node NodeA
    public Node NodeB
}

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

Если вы на самом деле не храните какие-либо данные в филиале (более обычно это называется Edge), то вам это вообще не нужно. Узлы могут просто вести список других узлов, с которыми они связываются.

0 голосов
/ 28 апреля 2010

Я рекомендую сделать так, чтобы только ваш Граф знал о ветвях и узлах. Таким образом, вы можете контролировать доступ и убедиться, что знаете, как сделать недействительными все ваши собственные ссылки. Если вам необходимо предоставить доступ к пользовательским данным на узле, вы можете предоставить методы для итерации по своей структуре, а не предоставлять доступ к необработанной структуре. Вы можете встраивать информацию о пользователях в классы структуры с помощью обобщений (то есть определяемого пользователем свойства «Tag» для каждого узла и каждой ветви).

0 голосов
/ 28 апреля 2010

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

class Branch
{
    public Branch(Node nodeA, Node nodeB) { NodeA = nodeA; NodeB = nodeB; }
    public Node NodeA { get; set; }
    public Node NodeB { get; set; }
}

class Node
{
    public Node(string name) { Name = name; }
    public string Name { get; set; }
}

...

List<Node> nodes = new List<Node>() { new Node("Apple"), new Node("Banana") };
List<Branch> branches = new List<Branch>() { new Branch(nodes[0], nodes[1]), new Branch(nodes[1], nodes[0]) };

Node node = nodes[0];
nodes.Remove(node);

var query = from branch in branches
            where branch.NodeA == node || branch.NodeB == node 
            select branch;

foreach (Branch branch in query)
{
    if (branch.NodeA == node)
        branch.NodeA = null;
    if (branch.NodeB == node) // could just be 'else' if NodeA cannot equal NodeB
        branch.NodeB = null;
}

Что отлично подходит для удаления ссылок в вашем списке ветвей. Однако, как указывает Мехрдад, становится все труднее уничтожить все ссылки, если ссылки на ваш объект Node более плодовиты.

0 голосов
/ 28 апреля 2010

Попробуйте WeakReference в качестве оболочки для Node или Branch, списки будут содержать эти слабые ссылки.

0 голосов
/ 28 апреля 2010

Некоторому классу, содержащему ссылку на узел, это не понравится, если какой-то механизм просто удалит эту ссылку. И нет, другого пути нет. Вы должны выполнить итерацию и установить их в null вручную. Но если Node представляет собой ограниченный или запоминающийся интенсивный ресурс, вам следует рассмотреть возможность более удобного доступа к нему, возможно, в центральном месте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...