Tôi yêu bộ sưu tập không thay đổi nhưng chúng thường cảm thấy như một giải pháp cần sự cố. Chúng có thể là unexpectedly slow do how they are implemented: chúng cố gắng hết sức để sử dụng bộ nhớ hiệu quả với chi phí làm cho các vòng lặp và lập chỉ mục phức tạp hơn về mặt thuật toán một cách phức tạp hơn. Với trí nhớ phong phú như thế nào, nó có thể khó để biện minh cho chi phí đó. Có rất nhiều thông tin được tìm thấy trên web về việc sử dụng thành công các bộ sưu tập không thể thay đổi (trừ ImmutableArray<T>
). Trong một nỗ lực để khắc phục điều đó, tôi nghĩ rằng tôi muốn thêm một câu trả lời cho câu hỏi 3 năm tuổi này về một trường hợp mà tôi thấy ImmutableStack<T>
là thực sự hữu ích.
xem xét cây giống như cấu trúc này rất cơ bản:
public class TreeNode
{
public TreeNode Parent { get; private set; }
public List<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
Parent = parent;
}
}
tôi đã tạo ra các lớp học theo mô hình cơ bản này với nhiều người, nhiều lần, và trong thực tế, nó luôn luôn làm việc cho tôi. Gần đây, tôi đã bắt đầu làm điều gì đó như thế này:
public class TreeNode
{
public ImmutableStack<TreeNode> NodeStack { get; private set; }
public ImmutableStack<TreeNode> ParentStack { get { return NodeStack.IsEmpty ? ImmutableStack<TreeNode>.Empty : NodeStack.Pop(); } }
public TreeNode Parent { get { return ParentStack.IsEmpty ? null : ParentStack.Peek(); } }
public ImmutableArray<TreeNode> ChildNodes { get; set; }
public TreeNode(TreeNode parent)
{
if (parent == null)
NodeStack = ImmutableStack<TreeNode>.Empty;
else
NodeStack = parent.NodeStack.Push(this);
}
}
Đối với nhiều lý do, tôi đã đến để thích lưu trữ một ImmutableStack<T>
của TreeNode
trường hợp mà tôi có thể đi qua vào thư mục gốc chứ không phải chỉ đơn giản là một tài liệu tham khảo với phiên bản Parent
.
ImmutableStack<T>
'về triển khai cơ bản là danh sách được liên kết. Mỗi thể hiện của ImmutableStack<T>
thực sự là một nút trên danh sách được liên kết. Đó là lý do tại sao tài sản ParentStack
chỉ là Pop
's NodeStack
. ParentStack
sẽ trả về cùng một thể hiện của ImmutableStack<T>
là Parent.NodeStack
.
- Nếu bạn lưu trữ tham chiếu đến
Parent
TreeNode
, sẽ dễ dàng tạo vòng lặp có thể gây ra các hoạt động như tìm nút gốc để không bao giờ chấm dứt và bạn sẽ bị tràn ngăn xếp hoặc vòng lặp vô hạn. An ImmutableStack<T>
, mặt khác, có thể được tăng lên bởi Pop
-ing, đảm bảo rằng nó sẽ chấm dứt.
- Thực tế là bạn sử dụng một bộ sưu tập (
ImmutableStack<T>
) có nghĩa là bạn có thể ngăn chặn vòng tròn xảy ra ở nơi đầu tiên bằng cách đơn giản để đảm bảo các parent
TreeNode
không xảy ra hai lần khi xây dựng TreeNode
.
Đó chỉ là bắt đầu. Tôi nghĩ rằng các ImmutableStack<T>
làm cho hoạt động cây đơn giản và an toàn hơn. Bạn có thể (an toàn) sử dụng LINQ trên đó. Bạn có muốn biết liệu một người TreeNode
có phải là hậu duệ của người khác không? Bạn chỉ có thể làm ParentStack.Any(node => node == otherNode)
Tổng quát hơn, lớp ImmutableStack<T>
là dành cho bất kỳ trường hợp bạn muốn một Stack<T>
mà bạn có thể đảm bảo sẽ không bao giờ được sửa đổi, hoặc bạn cần một cách dễ dàng để có được một "bản chụp" của một Stack<T>
nhưng không muốn tạo ra một loạt các bản sao của ngăn xếp đó. Nếu bạn có một loạt các bước lồng nhau, bạn có thể sử dụng một số ImmutableStack<T>
để theo dõi tiến trình và vì vậy khi bước hoàn tất, nó có thể chuyển công việc sang bước cha mẹ của nó. Tính chất bất biến của nó đặc biệt hữu ích khi bạn đang cố gắng làm việc song song.
Nguồn
2017-07-05 22:30:01
http://blogs.msdn.com/b/ericlippert/archive/2007/12/04/immutability-in-c-part-two-a-simple-immutable-stack.aspx –
Có, tôi đã thấy bài viết đó. Nhưng nó không cung cấp một lý do tại sao một ngăn xếp bất biến có thể hữu ích trong một số ngữ cảnh. Một lần nữa, đó có thể là sự hiểu biết của tôi về bất biến đó là sai. –