Làm cách nào để tạo một Lớp bất biến?


113

Tôi đang làm việc để tạo ra một lớp bất biến.
Tôi đã đánh dấu tất cả các thuộc tính là chỉ đọc.

Tôi có một danh sách các mục trong lớp.
Mặc dù nếu thuộc tính chỉ đọc, danh sách có thể được sửa đổi.

Việc để lộ IEnumerable của danh sách làm cho nó trở nên bất biến.
Tôi muốn biết các quy tắc cơ bản mà người ta phải tuân theo để làm cho một lớp bất biến là gì?


2
Tôi thực sự khuyên bạn nên đọc loạt blog của Eric Lippert về tính bất biến , đặc biệt là mục về "các loại bất biến" . Nhận xét của bạn rằng "Để lộ IEnumerable của danh sách làm cho nó bất biến" có vẻ hơi lạ đối với tôi. Ý bạn là gì?
Jon Skeet

Ý tôi là, thay vì cho phép truy cập danh sách (Nếu đối tượng có danh sách một số đối tượng khác), cho phép người dùng truy cập vào các thành viên bằng IEnumerable. Danh sách nói ở đây như trong một ví dụ cụ thể, nhưng nó có thể là bất kỳ cấu trúc dữ liệu nào.
Biswanath

1
Các liên kết của Jon Skeet đến loạt blog của Eric Lippert đã bị hỏng khi MSDN lưu trữ các blog này. Vui lòng xem "các loại bất biến" . Phần còn lại của loạt phim dường như nằm dưới tháng 12 năm 2007 + tháng 1 năm 2008 trong bảng điều khiển bên trái.
John Doe

Xin vui lòng xem loạt blog của Eric Lippert về cách mọi người thường nhầm lẫn giữa các điều khoản atomicity, volatilityimmutability: Phần thứ nhất , Phần thứ hai , và Phần thứ ba . Đây là từ blog cá nhân của anh ấy và, tôi tin rằng, thân thiện với người mới hơn các bài đăng trên MSDN của anh ấy.
John Doe

Câu trả lời:


121

Tôi nghĩ bạn đang đi đúng hướng -

  • tất cả thông tin được đưa vào lớp phải được cung cấp trong hàm tạo
  • tất cả các tài sản chỉ nên là getters
  • nếu một tập hợp (hoặc Mảng) được truyền vào phương thức khởi tạo, nó sẽ được sao chép để ngăn người gọi sửa đổi nó sau này
  • nếu bạn định trả lại bộ sưu tập của mình, hãy trả lại bản sao hoặc phiên bản chỉ đọc (ví dụ: sử dụng ArrayList.ReadOnly hoặc tương tự - bạn có thể kết hợp điểm này với điểm trước đó và lưu trữ bản sao chỉ đọc để được trả lại khi người gọi truy cập nó), trả về một điều tra viên hoặc sử dụng một số phương thức / thuộc tính khác cho phép truy cập chỉ đọc vào bộ sưu tập
  • Hãy nhớ rằng bạn vẫn có thể có sự xuất hiện của một lớp có thể thay đổi nếu bất kỳ thành viên nào của bạn có thể thay đổi - nếu trường hợp này xảy ra, bạn nên sao chép bất kỳ trạng thái nào bạn muốn giữ lại và tránh trả lại toàn bộ các đối tượng có thể thay đổi, trừ khi bạn sao chép chúng trước khi trả lại chúng cho người gọi - một tùy chọn khác là chỉ trả lại các "phần" bất biến của đối tượng có thể thay đổi - cảm ơn @Brian Rasmussen đã khuyến khích tôi mở rộng điểm này

Tôi có nên viết một thuộc tính tra cứu trình bao bọc, điều này cho phép bạn chỉ thực hiện việc tra cứu? Và cảm ơn vì câu trả lời.
Biswanath

5
Bất kỳ kiểu tham chiếu có thể thay đổi nào được truyền làm đối số cho hàm tạo đều phải được sao chép. Nếu không, người gọi sẽ vẫn giữ một tham chiếu đến trạng thái.
Brian Rasmussen

@Brian Rasmussen, bạn nói đúng, nhưng ngay cả khi nó được sao chép, bất kỳ người gọi nào cũng có thể truy cập vào đối tượng có thể thay đổi, tùy thuộc vào acces.sors được cung cấp. Trong trường hợp được thông qua một đối tượng có thể thay đổi, đó là lớp tốt nhất off luôn trả về một bản sao khác nhau, hoặc phần bất biến của đối tượng
Blair Conrad

@Blair - Nếu tôi có từ điển trong lớp mà tôi chỉ muốn sử dụng nó để tra cứu. Thuộc tính chỉ đọc mà khi tra cứu có ổn không?
Biswanath 09/08/08

@Biswanath - vâng, với sự báo trước rằng nếu các giá trị trong từ điển có thể thay đổi, bạn sẽ cần phải bảo vệ họ trước khi trở về, nếu bạn không muốn họ bị đột biến
Blair Conrad

18

Để không thay đổi, tất cả các thuộc tính và trường của bạn phải được đọc. Và các mục trong bất kỳ danh sách nào cũng phải là bất biến.

Bạn có thể tạo thuộc tính danh sách chỉ đọc như sau:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}

1
Tôi nghĩ ImmutableList sẽ tốt hơn.
Kevin Wong

sử dụng ImmutableList, đồng thời cân nhắc niêm phong lớp
kofifus 13/12/18

Tôi không tin rằng ImmutableList là một sự phù hợp tốt đối với trường hợp sử dụng này: basic rules to make a class (that contains a list) immutable. Ngữ nghĩa của ImmutableList cho phép sử dụng bộ nhớ hiệu quả hơn khi thêm các mục vào bản sao của danh sách, nhưng đối với tôi đó dường như là một trường hợp sử dụng cụ thể hơn.
Joe

11

Ngoài ra, hãy nhớ rằng:

public readonly object[] MyObjects;

không phải là bất biến ngay cả khi nó được đánh dấu bằng từ khóa chỉ đọc. Bạn vẫn có thể thay đổi các tham chiếu / giá trị mảng riêng lẻ bằng trình truy cập chỉ mục.


5

Sử dụng ReadOnlyCollectionlớp học. Nó nằm trong System.Collections.ObjectModelkhông gian tên.

Trên bất kỳ thứ gì trả về danh sách của bạn (hoặc trong hàm tạo), hãy đặt danh sách dưới dạng một tập hợp chỉ đọc.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}

1

Một tùy chọn khác là sử dụng mẫu khách truy cập thay vì hiển thị bất kỳ bộ sưu tập nội bộ nào.


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.