2010-04-27 46 views
7

Tôi đang đối mặt với một tình huống mà tôi có các đối tượng phụ thuộc và tôi muốn có thể xóa một đối tượng và tất cả các tham chiếu đến đối tượng đó.Xóa các mục khỏi danh sách và tất cả các tham chiếu đến chúng

Giả sử tôi có cấu trúc đối tượng như mã bên dưới, với loại Chi nhánh tham chiếu hai Nút.

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

Nếu tôi loại bỏ một Node từ danh sách các nút trong lớp Graph, nó vẫn còn có thể là một hoặc nhiều đối tượng chi nhánh vẫn còn chứa một tham chiếu đến Node loại bỏ, do đó duy trì nó trong bộ nhớ, trong khi thực sự những gì tôi sẽ khá giống như sẽ được thiết lập bất kỳ tham chiếu đến các Node loại bỏ null và để cho đá thu gom rác thải trong.

Ngoài liệt kê qua từng chi nhánh và kiểm tra mỗi Node tuần tự tham khảo, được có bất kỳ ý tưởng thông minh về cách tôi loại bỏ tài liệu tham khảo với Node trong mỗi thể hiện Branch và thực sự là bất kỳ lớp nào khác tham chiếu đến Node đã loại bỏ?

+1

Bạn có thực sự lưu trữ bất kỳ dữ liệu nào trên Chi nhánh không? Nếu không, bạn có thể loại bỏ hoàn toàn lớp đó và chỉ lưu trữ các nút liên quan trên lớp Node. –

+0

Xin chào, Có, tôi đang lưu trữ một số dữ liệu khác, cấu trúc dữ liệu ở trên chỉ là một ví dụ đơn giản để hiển thị nơi tham chiếu của mô hình thực tế của tôi. – LiamV

Trả lời

1

Thay đổi Node bạn để bao gồm một danh sách các chi nhánh đó là ngày:

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 
} 

Bây giờ lớp Graph của bạn không cần một danh sách của Nodes hoặc Chi nhánh, tất cả những gì cần là một gốc Node duy nhất. Khi bạn loại bỏ một Nút, bạn xóa tất cả các nhánh khỏi nó. Rõ ràng bạn đóng gói tất cả các phương thức để thêm và loại bỏ các nút và các nhánh để mã bên ngoài không thể phá vỡ cấu trúc.

Nếu bạn không thực sự lưu trữ bất kỳ dữ liệu nào trên nhánh (thường được gọi là cạnh) bạn không cần nó chút nào. Các nút chỉ có thể duy trì danh sách các nút khác mà chúng liên kết trong và ngoài.

+0

Bạn đúng là tôi không cần danh sách các nút hoặc các nhánh dựa trên thông tin tôi đã cung cấp, nhưng ở đó là những yếu tố khác mà bạn có thể sẽ không biết để xem xét. Ví dụ, dữ liệu này thực sự sẽ được lưu trữ trong một cơ sở dữ liệu, do đó sẽ thích hợp hơn nếu có danh sách của từng loại, ánh xạ tới bảng cơ sở dữ liệu. – LiamV

+0

Cấu trúc này có thể * dễ dàng * ánh xạ tới cơ sở dữ liệu: bảng cho các nút, bảng cho các cạnh, mối quan hệ FK giữa chúng. Đó là một mô hình quan hệ 100%. EF 4 có thể xử lý tất cả các vấn đề xung quanh việc tải các cạnh và nút không cần thêm bất kỳ công việc nào. Bạn nhận được Node.Edges tự động, miễn phí, khi bạn nhập cơ sở dữ liệu vào một Mô hình Dữ liệu Thực thể. Trong thực tế nó sẽ làm cho xóa dễ dàng quá, khi bạn loại bỏ một Edge nó có thể tự động loại bỏ nó từ mỗi nút. –

0

Một số lớp giữ tham chiếu đến Nút sẽ không thích, nếu một số cơ chế chỉ xóa tham chiếu này. Và không, không có cách nào khác. Bạn phải lặp lại và đặt chúng thành null theo cách thủ công. Nhưng nếu Node đại diện cho một tài nguyên chuyên sâu hạn chế hoặc ghi nhớ, bạn nên xem xét việc quản lý nó tốt hơn, có lẽ ở một vị trí trung tâm.

+0

Cảm ơn rất nhiều vì tất cả các câu trả lời của bạn, nó chắc chắn đã cho tôi điều gì đó để suy nghĩ. Tôi nghĩ rằng tôi sẽ đi xuống con đường mà Hightechrider đã gợi ý, cho đến khi tôi gặp phải một số vấn đề. Vì lý do đó tôi đã chấp nhận câu trả lời của anh ấy. – LiamV

6

Không có tính năng ngôn ngữ C# được tích hợp sẵn để tạo điều kiện thuận lợi cho bạn (bạn không thể theo dõi bài tập). Bạn sẽ phải theo dõi tất cả các tham chiếu ở đâu đó và cập nhật nó ngay khi bạn chỉ định một tham chiếu mới cho nó. Một ý tưởng rất chung là cung cấp sự kiện Removed trong chính số Node và tăng sự kiện khi đối tượng được cho là bị bỏ rơi. Mỗi khi bạn muốn giữ một tham chiếu mới đến Node, bạn sẽ đăng ký sự kiện với một đại biểu phù hợp mà không tham chiếu đến đối tượng đó. Tất nhiên, nếu bạn đang làm điều này với một tập hợp các loại đã biết trước đó tham khảo nút theo một cách cụ thể, có thể có những cách dễ dàng và hiệu quả hơn để hoàn thành nhiệm vụ.

0

Hãy thử WeakReference làm trình bao bọc cho Nút hoặc Chi nhánh, danh sách sẽ chứa các tham chiếu yếu này.

+1

'WeakReference' sẽ loại bỏ các vấn đề thu gom rác nhưng nó sẽ không giải quyết được vấn đề nếu bạn cần cập nhật các cấu trúc dữ liệu một cách thích hợp ở những nơi khác nhau ngay khi nút bị xóa. –

+0

để thực hiện các sự kiện OnAdded, OnRemoved và giữ tham chiếu ngược - vì vậy mỗi Node sẽ biết về một chi nhánh. –

0

Bạn chắc chắn có thể truy vấn các yếu tố chi nhánh của bạn để tham khảo cho mỗi nút mà bạn đang loại bỏ, một cái gì đó giống như ví dụ này

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

Đó là tốt cho việc loại bỏ tài liệu tham khảo trong danh sách các chi nhánh của bạn. Tuy nhiên, như Mehrdad chỉ ra, nó ngày càng trở nên khó khăn hơn để quét sạch tất cả các tài liệu tham khảo nếu tham chiếu đến đối tượng Node của bạn là nhiều hơn.

0

Tôi khuyên bạn nên tạo nó để chỉ Đồ thị của bạn biết về Chi nhánh và Nút. Bằng cách đó, bạn có thể kiểm soát quyền truy cập và có thể đảm bảo rằng bạn biết cách vô hiệu hóa tất cả các tham chiếu của riêng bạn. Nếu bạn cần cung cấp quyền truy cập vào dữ liệu người dùng tại Nút, bạn có thể cung cấp các phương thức để lặp qua cấu trúc của bạn, thay vì cấp quyền truy cập vào cấu trúc thô. Bạn có thể nhúng thông tin người dùng vào các lớp cấu trúc thông qua Generics (tức là thuộc tính 'Tag' do người dùng định nghĩa cho mỗi Nút và mỗi Chi nhánh).

Các vấn đề liên quan