Struktura jest w swej istocie niczym więcej, ani mniej niż skupiskiem pól. W .NET możliwe jest, aby struktura "udawała" obiekt, a dla każdego typu struktury .NET domyślnie definiuje typ obiektu sterty z tymi samymi polami i metodami, które - będąc obiektem sterty - będą zachowywać się jak obiekt . Zmienna, która zawiera odniesienie do takiego obiektu sterty (struktura „pudełkowa”) będzie wykazywać semantykę referencyjną, ale ta, która przechowuje bezpośrednio strukturę, jest po prostu agregacją zmiennych.
Myślę, że wiele zamieszania między strukturami a klasami wynika z faktu, że struktury mają dwa bardzo różne przypadki użycia, które powinny mieć bardzo różne wytyczne projektowe, ale wytyczne MS nie rozróżniają między nimi. Czasami istnieje potrzeba czegoś, co zachowuje się jak przedmiot; w takim przypadku wytyczne MS są całkiem rozsądne, chociaż „limit 16 bajtów” powinien prawdopodobnie być bardziej zbliżony do 24-32. Czasami jednak potrzebna jest agregacja zmiennych. Struktura używana w tym celu powinna po prostu składać się z kilku pól publicznych i prawdopodobnie plikuEquals
przesłonięcia, ToString
przesłonięcia iIEquatable(itsType).Equals
realizacja. Struktury używane jako agregacje pól nie są obiektami i nie powinny udawać, że są. Z punktu widzenia struktury pole nie powinno oznaczać nic więcej ani mniej niż „ostatnia rzecz zapisana w tym polu”. Każde dodatkowe znaczenie powinno być określone przez kod klienta.
Na przykład, jeśli struktura agregująca zmienne ma członków Minimum
i Maximum
, sama struktura nie powinna tego obiecać Minimum <= Maximum
. Kod, który otrzymuje taką strukturę jako parametr, powinien zachowywać się tak, jakby był przekazywany oddzielnie Minimum
i Maximum
wartości. Wymaganie, które Minimum
nie jest większe niż Maximum
powinno być traktowane jako wymaganie, aby Minimum
parametr nie był większy niż parametr przekazywany oddzielnie Maximum
.
Przydatnym wzorcem do rozważenia jest czasami ExposedHolder<T>
zdefiniowanie klasy, na przykład:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Jeśli ktoś ma List<ExposedHolder<someStruct>>
, gdzie someStruct
jest strukturą agregującą zmienne, można zrobić takie rzeczy, jak myList[3].Value.someField += 7;
, ale przekazanie myList[3].Value
innemu kodowi da mu zawartość, Value
a nie da mu możliwości zmiany. Z drugiej strony, gdyby ktoś użył a List<someStruct>
, należałoby użyć var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Gdyby ktoś użył zmiennego typu klasy, ujawnienie zawartości myList[3]
kodu z zewnątrz wymagałoby skopiowania wszystkich pól do innego obiektu. Gdyby ktoś użył niezmiennego typu klasy lub struktury „w stylu obiektowym”, należałoby skonstruować nową instancję, która byłaby podobna, myList[3]
z someField
tą różnicą, że była inna, a następnie zapisać tę nową instancję na liście.
Jedna dodatkowa uwaga: jeśli przechowujesz dużą liczbę podobnych rzeczy, dobrym rozwiązaniem może być przechowywanie ich w potencjalnie zagnieżdżonych tablicach struktur, najlepiej starając się zachować rozmiar każdej tablicy w przedziale od 1 KB do 64 KB lub więcej. Tablice struktur są szczególne, ponieważ indeksowanie da bezpośrednie odniesienie do struktury wewnątrz, więc można powiedzieć „a [12] .x = 5;”. Chociaż można zdefiniować obiekty podobne do tablic, C # nie pozwala im na współdzielenie takiej składni z tablicami.