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, ToStringprzesłonięcia iIEquatable(itsType).Equalsrealizacja. 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 Minimumi 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 Minimumi Maximumwartości. Wymaganie, które Minimumnie jest większe niż Maximumpowinno być traktowane jako wymaganie, aby Minimumparametr 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 someStructjest strukturą agregującą zmienne, można zrobić takie rzeczy, jak myList[3].Value.someField += 7;, ale przekazanie myList[3].Valueinnemu kodowi da mu zawartość, Valuea 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 someFieldtą 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.