Về cơ bản, cấu trúc là một tập hợp các trường. Trong .NET, một cấu trúc có thể "giả vờ" là một đối tượng và đối với mỗi kiểu cấu trúc .NET định nghĩa ngầm một kiểu đối tượng heap với các trường và phương thức giống nhau - là một đối tượng heap - sẽ hoạt động như một đối tượng . Một biến chứa một tham chiếu đến một đối tượng đống như vậy (cấu trúc "đóng hộp") sẽ thể hiện ngữ nghĩa tham chiếu, nhưng một biến chứa một cấu trúc trực tiếp chỉ đơn giản là một tập hợp các biến.
Tôi nghĩ rằng phần lớn sự nhầm lẫn giữa cấu trúc và lớp bắt nguồn từ thực tế là các cấu trúc có hai trường hợp sử dụng rất khác nhau, nên có các hướng dẫn thiết kế rất khác nhau, nhưng các hướng dẫn MS không phân biệt giữa chúng. Đôi khi cần có một thứ gì đó hoạt động như một vật thể; trong trường hợp đó, các hướng dẫn MS khá hợp lý, mặc dù "giới hạn 16 byte" có lẽ giống 24-32 hơn. Tuy nhiên, đôi khi điều cần thiết là tổng hợp các biến. Một cấu trúc được sử dụng cho mục đích đó chỉ nên bao gồm một loạt các trường công khai và có thể Equals
ghi đè, ToString
ghi đè vàIEquatable(itsType).Equals
thực hiện. Các cấu trúc được sử dụng làm tập hợp các trường không phải là đối tượng và không nên giả vờ như vậy. Theo quan điểm của cấu trúc, ý nghĩa của trường không nên hơn hoặc kém hơn "thứ cuối cùng được ghi vào trường này". Bất kỳ ý nghĩa bổ sung nào phải được xác định bởi mã khách hàng.
Ví dụ: nếu một cấu trúc tổng hợp biến có các thành viên Minimum
và Maximum
, bản thân cấu trúc sẽ không hứa hẹn điều đó Minimum <= Maximum
. Mã nhận được cấu trúc như một tham số sẽ hoạt động như thể nó được truyền các giá trị Minimum
và Maximum
giá trị riêng biệt . Một yêu cầu Minimum
không lớn hơn Maximum
sẽ được coi như một yêu cầu rằng một Minimum
tham số không được lớn hơn một tham số được chuyển riêng Maximum
.
Một mô hình hữu ích đôi khi cần xem xét là có một ExposedHolder<T>
lớp được định nghĩa như sau:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Nếu một người có a List<ExposedHolder<someStruct>>
, đâu someStruct
là cấu trúc tổng hợp biến, người ta có thể làm những việc như thế myList[3].Value.someField += 7;
, nhưng việc đưa cho myList[3].Value
mã khác sẽ cung cấp cho nó nội dung Value
thay vì cung cấp cho nó một phương tiện để thay đổi nó. Ngược lại, nếu một người đã sử dụng a List<someStruct>
, thì nó sẽ cần thiết để sử dụng var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Nếu một người sử dụng loại lớp có thể thay đổi, việc hiển thị nội dung của myList[3]
mã bên ngoài sẽ yêu cầu sao chép tất cả các trường sang một số đối tượng khác. Nếu một người đã sử dụng kiểu lớp không thay đổi hoặc cấu trúc "kiểu đối tượng", thì cần phải xây dựng một thể hiện mới giống như myList[3]
ngoại trừ someField
nó khác và sau đó lưu trữ thể hiện mới đó vào danh sách.
Một lưu ý bổ sung: Nếu bạn đang lưu trữ một số lượng lớn những thứ tương tự, có thể tốt nếu lưu trữ chúng trong các mảng cấu trúc có thể lồng vào nhau, tốt hơn là cố gắng giữ kích thước của mỗi mảng trong khoảng từ 1K đến 64K hoặc hơn. Mảng cấu trúc là đặc biệt, trong việc lập chỉ mục đó, người ta sẽ đưa ra một tham chiếu trực tiếp đến một cấu trúc bên trong, vì vậy người ta có thể nói "a [12] .x = 5;". Mặc dù người ta có thể định nghĩa các đối tượng giống mảng, nhưng C # không cho phép chúng chia sẻ cú pháp như vậy với mảng.