Các cấu trúc với các trường hoặc thuộc tính có thể thay đổi công khai không phải là xấu.
Các phương thức cấu trúc (khác với các setters thuộc tính) đột biến "cái này" là hơi xấu, chỉ vì .net không cung cấp một phương tiện để phân biệt chúng với các phương thức không. Các phương thức cấu trúc không làm thay đổi "cái này" sẽ là bất khả xâm phạm ngay cả trên các cấu trúc chỉ đọc mà không cần sao chép phòng thủ. Các phương thức làm đột biến "cái này" hoàn toàn không nên bất khả xâm phạm trên các cấu trúc chỉ đọc. Vì .net không muốn cấm các phương thức cấu trúc không sửa đổi "điều này" khỏi việc được gọi trên các cấu trúc chỉ đọc, nhưng không muốn cho phép các cấu trúc chỉ đọc bị đột biến, nên nó sao chép các cấu trúc trong đọc chỉ bối cảnh, được cho là tồi tệ nhất của cả hai thế giới.
Mặc dù có vấn đề với việc xử lý các phương thức tự biến đổi trong bối cảnh chỉ đọc, tuy nhiên, các cấu trúc có thể thay đổi thường cung cấp ngữ nghĩa vượt trội hơn nhiều so với các loại lớp có thể thay đổi. Hãy xem xét ba chữ ký phương pháp sau:
struct PointySturation {public int x, y, z;};
lớp PointyClass {công khai int x, y, z;};
void Phương thức1 (PointySturation foo);
void Phương thức 2 (ref PointySturation foo);
void Phương thức3 (PointyClass foo);
Đối với mỗi phương pháp, trả lời các câu hỏi sau:
- Giả sử phương thức không sử dụng bất kỳ mã "không an toàn" nào, nó có thể sửa đổi foo không?
- Nếu không có tham chiếu bên ngoài nào đến 'foo' trước khi phương thức được gọi, thì có thể tồn tại tham chiếu bên ngoài không?
Đáp án:
Câu 1 ::
Method1()
không (ý định rõ ràng)
Method2()
: có (ý định rõ ràng)
Method3()
: có (ý định không chắc chắn)
Câu 2
Method1()
:: không
Method2()
: không (trừ khi không an toàn)
Method3()
: có
Phương thức 1 không thể sửa đổi foo và không bao giờ có được tài liệu tham khảo. Phương thức 2 có một tham chiếu ngắn đến foo, nó có thể sử dụng sửa đổi các trường của foo bất kỳ số lần nào, theo bất kỳ thứ tự nào cho đến khi trả về, nhưng nó không thể duy trì tham chiếu đó. Trước khi Phương thức 2 trả về, trừ khi nó sử dụng mã không an toàn, mọi bản sao có thể được tạo từ tham chiếu 'foo' của nó sẽ biến mất. Phương thức 3, không giống như Phương thức 2, có một tài liệu tham khảo có thể chia sẻ bừa bãi cho foo và không biết nó có thể làm gì với nó. Nó có thể không thay đổi foo chút nào, nó có thể thay đổi foo và sau đó quay trở lại, hoặc nó có thể đưa ra một tham chiếu đến foo đến một chủ đề khác có thể thay đổi nó theo một cách tùy tiện vào một thời điểm tùy ý trong tương lai.
Mảng cấu trúc cung cấp ngữ nghĩa tuyệt vời. Với RectArray [500] loại Hình chữ nhật, rõ ràng và rõ ràng làm thế nào để sao chép phần tử 123 sang phần tử 456 và sau đó một thời gian sau đó đặt chiều rộng của phần tử 123 thành 555, mà không làm phiền phần tử 456. "RectArray [432] = RectArray [321] ]; ...; RectArray [123] .Width = 555; ". Biết rằng Hình chữ nhật là một cấu trúc với trường số nguyên có tên là Width sẽ cho mọi người biết tất cả những gì cần biết về các câu lệnh trên.
Bây giờ, giả sử RectClass là một lớp có cùng các trường với Hình chữ nhật và người ta muốn thực hiện các thao tác tương tự trên RectClassArray [500] loại RectClass. Có lẽ mảng được cho là chứa 500 tham chiếu bất biến được khởi tạo trước cho các đối tượng RectClass có thể thay đổi. trong trường hợp đó, mã thích hợp sẽ là một cái gì đó như "RectClassArray [321]. SetBound (RectClassArray [456]); ...; RectClassArray [321] .X = 555;". Có lẽ mảng được giả sử giữ các trường hợp sẽ không thay đổi, vì vậy mã phù hợp sẽ giống như "RectClassArray [321] = RectClassArray [456]; ...; RectClassArray [321] = New RectClass (RectClassArray [321] ]); RectClassArray [321] .X = 555; " Để biết người ta phải làm gì, người ta sẽ phải biết nhiều hơn cả về RectClass (ví dụ: nó có hỗ trợ trình tạo bản sao, phương thức sao chép, v.v. ) và mục đích sử dụng của mảng. Không nơi nào gần như sạch sẽ như sử dụng một cấu trúc.
Chắc chắn, không có cách nào hay cho bất kỳ lớp container nào ngoài một mảng để cung cấp ngữ nghĩa rõ ràng của một mảng cấu trúc. Điều tốt nhất có thể làm, nếu một người muốn một bộ sưu tập được lập chỉ mục với một chuỗi, có lẽ sẽ cung cấp một phương thức "ActOnItem" chung để chấp nhận một chuỗi cho chỉ mục, một tham số chung và một đại biểu sẽ được thông qua bằng cách tham chiếu cả tham số chung và mục bộ sưu tập. Điều đó sẽ cho phép gần như cùng một ngữ nghĩa như các mảng cấu trúc, nhưng trừ khi mọi người có thể theo đuổi vb.net và C # để cung cấp một cú pháp hay, mã sẽ trông có vẻ lộn xộn ngay cả khi nó có hiệu suất hợp lý (truyền tham số chung sẽ cho phép sử dụng một ủy nhiệm tĩnh và sẽ tránh mọi nhu cầu tạo bất kỳ thể hiện lớp tạm thời nào).
Cá nhân, tôi cảm thấy căm ghét Eric Lippert et al. phun ra liên quan đến các loại giá trị đột biến. Họ cung cấp ngữ nghĩa sạch hơn nhiều so với các loại tham chiếu lăng nhăng được sử dụng ở mọi nơi. Mặc dù có một số hạn chế với sự hỗ trợ của .net đối với các loại giá trị, có nhiều trường hợp các loại giá trị có thể thay đổi phù hợp hơn bất kỳ loại thực thể nào khác.
int
s,bool
s và tất cả các loại giá trị khác là xấu xa. Có những trường hợp cho tính đột biến và bất biến. Những trường hợp đó xoay quanh vai trò của dữ liệu, không phải kiểu phân bổ / chia sẻ bộ nhớ.