2010-05-10 36 views
34

Trong Design Guidelines for Developing Class Libraries, Microsoft nói:Tại sao Microsoft khuyên các trường chỉ đọc với các giá trị có thể thay đổi?

Do not assign instances of mutable types to read-only fields.

The objects created using a mutable type can be modified after they are created. For example, arrays and most collections are mutable types while Int32, Uri, and String are immutable types. For fields that hold a mutable reference type, the read-only modifier prevents the field value from being overwritten but does not protect the mutable type from modification.

này chỉ đơn giản là đạt lại hành vi của readonly mà không giải thích lý do tại sao nó xấu để sử dụng readonly. Hàm ý dường như là nhiều người không hiểu những gì "chỉ đọc" và sẽ mong đợi sai các trường chỉ đọc là không thay đổi sâu sắc. Trong thực tế, nó khuyên sử dụng "readonly" như tài liệu mã chỉ ra tính bất biến sâu - mặc dù thực tế là trình biên dịch không có cách nào để thực thi điều này - và không cho phép sử dụng nó cho hàm bình thường của nó: để đảm bảo rằng giá trị của trường không thay đổi sau đối tượng đã được xây dựng.

Tôi cảm thấy khó chịu với đề xuất này để sử dụng "chỉ đọc" để chỉ ra một điều gì đó khác với ý nghĩa bình thường của nó được hiểu bởi trình biên dịch. Tôi cảm thấy rằng nó khuyến khích mọi người hiểu lầm ý nghĩa của "chỉ đọc", và hơn nữa để mong đợi nó có nghĩa là một cái gì đó mà tác giả của mã có thể không có ý định. Tôi cảm thấy rằng nó ngăn cản việc sử dụng nó ở những nơi nó có thể hữu ích - ví dụ để cho thấy rằng một số mối quan hệ giữa hai đối tượng có thể thay đổi sẽ không thay đổi trong suốt vòng đời của một trong những đối tượng đó. Khái niệm giả định rằng người đọc không hiểu ý nghĩa của "chỉ đọc" cũng có vẻ mâu thuẫn với lời khuyên khác của Microsoft, chẳng hạn như quy tắc "Do not initialize unnecessarily" của FxCop, giả định độc giả mã của bạn là chuyên gia về ngôn ngữ và nên biết rằng (ví dụ) các trường bool được tự động khởi tạo thành false và dừng bạn cung cấp dự phòng cho thấy "có, điều này đã được đặt thành ý thức thành sai; tôi không quên khởi tạo nó".

Vì vậy, trước tiên và quan trọng nhất, tại sao Microsoft khuyên không nên sử dụng chỉ đọc để tham chiếu đến các loại có thể thay đổi? Tôi cũng muốn được biết:

  • Bạn có làm theo Hướng dẫn thiết kế này trong tất cả mã của bạn không?
  • Bạn mong đợi điều gì khi thấy "chỉ đọc" trong một đoạn mã bạn không viết?
+2

100% đồng ý. Hướng dẫn điển hình của Microsoft. \ * flush \ * –

+0

Tôi không thể nói rằng tôi không thiên vị. ;) Tôi cảm thấy rằng câu trả lời của Anthony, trong khi chính xác, không nói bất cứ điều gì chưa được nêu trong hướng dẫn: các tham chiếu chỉ đọc cho các đối tượng có thể thay đổi là xấu ... vì đối tượng có thể thay đổi. Mặt khác, câu trả lời của bạn ít nhất là giải thích tại sao bạn có thể muốn có tham chiếu chỉ đọc cho các đối tượng có thể thay đổi được. Bây giờ tôi nhìn vào nó, có lẽ câu trả lời của câu trả lời là - nói đúng - câu trả lời chính xác nhất cho câu hỏi tôi đã hỏi ban đầu. – Weeble

Trả lời

18

tôi đồng ý với bạn hoàn toàn, và tôi làm đôi khi sử dụng readonly trong mã của tôi đối với các loại tài liệu tham khảo có thể thay đổi.

Ví dụ: Tôi có thể có một số private hoặc protected thành viên - nói, một List<T> - mà tôi sử dụng trong phương pháp của một lớp trong tất cả vinh quang có thể thay đổi nó (gọi Add, Remove, vv). Tôi có thể chỉ đơn giản là muốn đặt một biện pháp bảo vệ tại chỗ để đảm bảo rằng, không có vấn đề gì, Tôi luôn luôn đối phó với cùng một đối tượng. Điều này bảo vệ cả tôi và các nhà phát triển khác không làm điều gì đó ngu ngốc: cụ thể là, gán thành viên cho một đối tượng mới.

Với tôi, đây thường là giải pháp thay thế thích hợp hơn để sử dụng thuộc tính với phương thức riêng tư set. Tại sao? Vì readonly có nghĩa là giá trị không thể thay đổi sau khi tạo bản đồ, ngay cả theo lớp cơ sở.

Nói cách khác, nếu tôi có điều này:

protected List<T> InternalList { get; private set; } 

Sau đó, tôi vẫn có thể thiết lập InternalList = new List<T>(); tại bất kỳ điểm tùy ý trong mã trong lớp cơ sở của tôi. (Điều này sẽ đòi hỏi một lỗi rất ngớ ngẩn trên một phần của tôi, vâng, nhưng nó vẫn sẽ có thể.)

Mặt khác, điều này:

protected readonly List<T> _internalList; 

Làm cho nó không lẫn đi đâu rõ ràng rằng _internalListlon chỉ bao giờ tham chiếu đến một đối tượng cụ thể (một đối tượng mà _internalList được đặt trong hàm tạo).

Vì vậy, tôi ở bên cạnh bạn. Ý tưởng mà người ta nên hạn chế sử dụng readonly trên một loại tham chiếu có thể thay đổi được gây phiền toái cho cá nhân tôi, vì nó cơ bản giả định một sự hiểu lầm của từ khóa readonly.

+0

+1, hoàn toàn đồng ý, riêng tư + chỉ đọc = ít lo lắng về mã. –

+0

Tôi sử dụng riêng tư chỉ đọc trên các loại tài liệu tham khảo. Nó là hoàn hảo để đảm bảo tôi không bao giờ chạm vào phụ thuộc của tôi trong một lớp học. Tôi không bao giờ mong đợi một lĩnh vực riêng tư chỉ đọc được là bất biến. Điều đó nói rằng tôi không hiểu hướng dẫn ms. Tôi nghĩ tốt hơn là nên tiếp tục sử dụng tính năng này thay vì bảo vệ các nhà phát triển không biết về các bài học mà họ chắc chắn nên học. – Console

25

Có vẻ như tự nhiên nếu trường là chỉ đọc, bạn sẽ không thể thay đổi giá trị hoặc mọi thứ phải làm với nó. Nếu tôi biết rằng Bar là một lĩnh vực readonly của Foo, tôi có thể rõ ràng là không nói

Foo foo = new Foo(); 
foo.Bar = new Baz(); 

Nhưng tôi có thể nhận được ngay với nói

foo.Bar.Name = "Blah"; 

Nếu đối tượng ủng hộ Bar là, trên thực tế, có thể thay đổi. Microsoft chỉ đơn giản là đề xuất chống lại rằng hành vi tinh vi, phản trực giác bằng cách gợi ý rằng các trường chỉ đọc được hỗ trợ bởi các đối tượng bất biến.

+1

Bạn nói rằng có vẻ tự nhiên, nhưng nó không có vẻ như vậy với tôi. Nó có vẻ * mong muốn * rằng có một số phương tiện xác định bất biến sâu sắc, nhưng dường như không * tự nhiên * mà chỉ đọc được, và thậm chí điều đó không rõ ràng dẫn đến kết luận rằng chúng ta nên cư xử * như thể ý nghĩa thực sự của người đọc khi nó không phải. – Weeble

2

Microsoft có một vài lời khuyên đặc biệt như vậy. Một trong những khác mà ngay lập tức lò xo để tâm trí không phải là tổ chung loại trong các thành viên công cộng, như List<List<int>>. Tôi cố gắng tránh những cấu trúc này ở nơi dễ dàng nhất có thể, nhưng bỏ qua lời khuyên thân thiện với người mới khi tôi cảm thấy việc sử dụng là hợp lý.

Đối với các trường chỉ đọc - Tôi cố gắng tránh các trường công khai như vậy, thay vào đó sẽ tìm các thuộc tính. Tôi nghĩ rằng có một lời khuyên về điều đó, nhưng quan trọng hơn là có trường hợp bây giờ và sau đó khi một lĩnh vực không hoạt động trong khi một tài sản nào (chủ yếu là nó đã làm với databinding và/hoặc thiết kế trực quan). Bằng cách làm cho tất cả các thuộc tính trường công khai, tôi tránh bất kỳ vấn đề tiềm ẩn nào.

+0

Bạn nêu ra một điểm tốt - nguyên tắc thiết kế chỉ áp dụng cho các thành viên không phải tư nhân? Các nguyên tắc đã nói không sử dụng các trường công cộng hoặc được bảo vệ. Tôi cho rằng họ đang tập trung vào API công cộng của một thư viện, mà trên đó các lĩnh vực tư nhân rõ ràng không có mang. Tôi chắc chắn không cảm thấy cần thiết phải có các trường có thể đọc được * công khai * chỉ đọc được vì tôi hiếm khi cảm thấy cần phải có các lĩnh vực công cộng. – Weeble

1

Cuối cùng, chúng chỉ là hướng dẫn.Tôi biết một thực tế là những người ở Microsoft thường không tuân theo tất cả các hướng dẫn.

7

DO NOT assign instances of mutable types to readonly fields.

tôi đã có một cái nhìn nhanh chóng trong Khung Hướng dẫn thiết kế cuốn sách (trang 161-162), và về cơ bản khẳng định những gì bạn đã nhận thấy chính mình. Có một bình luận thêm bởi Joe Duffy giải thích raison-d'être của phương châm:

What this guideline is trying to protect you from is believing you've exposed a deeply immutable object graph when in fact it is shallow, and then writing code that assumes the whole graph is immutable.
— Joe Duffy

Cá nhân tôi nghĩ rằng từ khóa readonly được mệnh danh là kinh khủng. Thực tế là nó chỉ xác định const-ness của tham chiếu, và không phải của const-ness của đối tượng được tham chiếu, dễ dàng tạo ra các tình huống gây nhầm lẫn.

Tôi nghĩ rằng nó sẽ thích hợp hơn nếu readonly làm cho các đối tượng tham chiếu không thay đổi, cũng không chỉ là tham chiếu, vì đó là những gì từ khóa ngụ ý.

Để khắc phục tình huống không may này, hướng dẫn đã được thực hiện.Trong khi tôi nghĩ rằng lời khuyên của nó là âm thanh theo quan điểm của con người (không phải lúc nào cũng rõ ràng loại nào có thể thay đổi và không phải là không tìm kiếm định nghĩa của chúng, và từ đó gợi ý không thay đổi sâu), đôi khi tôi muốn nói đến việc khai báo const-ness, C# sẽ cung cấp sự tự do tương tự như được cung cấp bởi C++, nơi bạn có thể định nghĩa const hoặc trên con trỏ, hoặc trên đối tượng trỏ tới hoặc trên cả hai hoặc không có gì.

+1

Trong khi tôi hiểu được quan điểm của bạn, một điều tôi chắc chắn * không * muốn là kiểu C++ kinh khủng 'const'. –

+0

_ @ JS Bangs: _ Tất nhiên điều này xảy ra theo sở thích cá nhân. Cú pháp khai báo kiểu C thực sự là ... tốt, "đặc biệt", nhưng cung cấp rất nhiều tính linh hoạt. '' '' Của C có thể không phù hợp với sự đơn giản tương đối của hệ thống kiểu C# /. NET ... nhưng đôi khi tôi bỏ lỡ nó. 'const' là một công cụ mạnh mẽ hơn nhiều người nghĩ. – stakx

+0

Được bỏ phiếu, nhưng tôi không đồng ý về một điểm mà chỉ đọc nên làm cho các đối tượng tham chiếu không thay đổi được. Tôi không thể thấy rằng làm việc mà không có một hệ thống phức tạp hơn cả const của C++: khi bạn truy cập một đối tượng thông qua một tham chiếu chỉ đọc, điều đó làm cho tất cả các thuộc tính và các trường của nó trả về các tham chiếu chỉ đọc? Điều gì về phương pháp trả về giá trị của nó? Bạn có cần một cách để khai báo một cách rõ ràng các phương thức const/readonly không? Làm thế nào nó hoạt động khi đối tượng là một kiểu được khai báo trong một assembly khác? – Weeble

1

Cú pháp bạn đang tìm kiếm được hỗ trợ bởi ngôn ngữ ++/CLI C:

const Example^ const obj; 

Các const đầu tiên làm cho các đối tượng tham chiếu không thay đổi, thứ 2 làm cho các tài liệu tham khảo không thay đổi. Cái sau tương đương với từ khóa chỉ đọc trong C#. Nỗ lực tránh né nó tạo ra lỗi biên dịch:

Test^ t = gcnew Test(); 
t->obj = gcnew Example(); // Error C3892 
t->obj->field = 42;   // Error C3892 
Example^ another = t->obj; // Error C2440 
another->field = 42; 

Tuy nhiên, đó là khói và gương. Tính bất biến được xác minh bởi trình biên dịch, không phải bởi CLR. Một ngôn ngữ được quản lý khác có thể sửa đổi cả hai. Đó là gốc của vấn đề, CLR chỉ không có hỗ trợ cho nó.

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