2010-04-27 17 views
7

Estoy frente a una situación en la que tengo objetos dependientes y me gustaría poder eliminar un objeto y todas las referencias a él.Eliminación de elementos de las listas y todas las referencias a ellos

Digamos que tengo una estructura de objeto como el siguiente, con un tipo de rama que hace referencia a dos nodos.

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

Si quito un nodo de la lista de nodos en la clase Graph, todavía es posible que uno o más objetos Branch todavía contiene una referencia al nodo eliminado, por lo que conserva en la memoria, mientras que en realidad lo que yo sería bastante útil establecer cualquier referencia al nódulo eliminado para anular y dejar que la recolección de basura inicie.

Aparte de enumerar a través de cada sucursal y revisar cada referencia de nodo secuencialmente, ¿hay alguna idea inteligente sobre cómo eliminar referencias al nodo en cada instancia de rama Y de hecho cualquier otra clase que haga referencia al nodo eliminado?

+1

¿Está almacenando datos en la Sucursal? Si no, puedes deshacerte de esa clase por completo y simplemente almacenar nodos relacionados en la clase Node. –

+0

Hola, sí, estoy almacenando algunos otros datos, la estructura de datos anterior era solo un ejemplo simple para mostrar dónde están las referencias de mi modelo real. – LiamV

Respuesta

1

Cambiar la Nodo de incluir una lista de las ramas Está en:

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 
} 

Ahora su clase gráfico no necesita una lista de nodos o ramas, todo lo que necesita es un solo nodo raíz. Cuando eliminas un Nodo, quitas todas las ramas de él. Claramente encapsulas todos los métodos para agregar y eliminar nodos y ramas para que el código externo no pueda romper la estructura.

Si no está almacenando ningún dato en la sucursal (más comúnmente llamado Edge) no lo necesita en absoluto. Los nodos pueden mantener una lista de los otros nodos a los que vinculan desde y hacia.

+0

Tiene razón en que no necesito una lista de nodos o ramas basada en la información que he dado, pero hay hay otros factores que probablemente no sepa considerar. Por ejemplo, estos datos se almacenarán en realidad en una base de datos, por lo tanto, es más apropiado tener listas de cada tipo, que se asignan a una tabla de base de datos. – LiamV

+0

Esta estructura puede * fácilmente * asignarse a una base de datos: tabla para Nodos, tabla para Bordes, relaciones FK entre ellos. Es 100% un modelo relacional. EF 4 puede manejar todos los problemas relacionados con la carga lenta de bordes y nodos sin ningún trabajo adicional. Obtiene Node.Edges automáticamente, de forma gratuita, cuando importa la base de datos a un Modelo de datos de entidad. De hecho, también facilitará la eliminación, cuando elimina un Edge puede eliminarlo automáticamente de cada Node. –

0

Alguna clase que tenga una referencia a un Nodo no le gustaría, si algún mecanismo simplemente elimina esta referencia. Y no, no hay otra manera. Debe iterar y configurarlos para que anulen manualmente. Pero si Node representa un recurso limitado o de gran intensidad de memoria, debería considerar gestionar mejor el acceso, tal vez en un lugar central.

+0

Muchas gracias por todas sus respuestas, ciertamente me ha dado algo en qué pensar. Creo que iré por la ruta sugerida por Hightechrider, hasta que encuentre algunos problemas. Por esa razón, he aceptado su respuesta. – LiamV

6

No hay función integrada de lenguaje C# para facilar eso (no se puede seguir realmente las asignaciones). Deberá realizar un seguimiento de todas las referencias en algún lugar y actualizarlas tan pronto como le asigne una nueva referencia. Una idea muy general es proporcionar un evento Removed en el propio Node y generar el evento cuando se supone que el objeto debe abandonarse. Cada vez que desee retener una nueva referencia al Node, se suscribirá al evento con un delegado correspondiente que anula la referencia a ese objeto. Por supuesto, si está haciendo esto con un conjunto de tipos previamente conocidos que hacen referencia al nodo de una manera específica, puede haber formas más fáciles y más eficientes de lograr la tarea.

0

Pruebe WeakReference como un contenedor para Nodo o Sucursal, las listas contendrán estas referencias débiles.

+1

'WeakReference' eliminará el problema de recolección de basura, pero no solucionará el problema si necesita actualizar las estructuras de datos de forma adecuada en diferentes lugares tan pronto como se elimine un nodo. –

+0

para implementar eventos OnAdded, OnRemoved y mantener referencias retrospectivas, para que cada nodo sepa sobre una sucursal. –

0

Por supuesto que puede consultar sus derivaciones parciales para las referencias a cada nodo que está eliminando, algo así como este ejemplo

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

Lo que está bien para la eliminación de las referencias en tu lista de ramas. Sin embargo, como señala Mehrdad, cada vez es más difícil eliminar todas las referencias si las referencias a su objeto Node son más prolíficas.

0

Te recomiendo que hagas que solo tu gráfico conozca sobre ramas y nodos. De esta forma puede controlar el acceso y puede asegurarse de saber cómo invalidar todas sus propias referencias. Si necesita proporcionar acceso a datos de usuario en un Nodo, puede proporcionar métodos para iterar sobre su estructura, en lugar de dar acceso a la estructura sin procesar. Puede incrustar información de usuario en las clases de estructura a través de genéricos (es decir, una propiedad 'Etiqueta' definida por el usuario para cada Nodo y cada Sucursal).

Cuestiones relacionadas