Najważniejszą różnicą między a class
a jest struct
to, co dzieje się w następującej sytuacji:
Thing thing1 = new Thing ();
thing1.somePropertyOrField = 5;
Rzecz rzecz 2 = rzecz 1;
thing2.somePropertyOrField = 9;
Jaki powinien być wpływ ostatniego stwierdzenia thing1.somePropertyOrField
? Jeśli Thing
struct i somePropertyOrField
jest odsłoniętym polem publicznym, obiekty thing1
i thing2
zostaną „oderwane” od siebie, więc to ostatnie zdanie nie będzie miało wpływu thing1
. Jeśli Thing
jest klasą, wtedy thing1
i thing2
będą do siebie przywiązane, a więc ta ostatnia instrukcja zapisze thing1.somePropertyOrField
. Należy użyć struktury w przypadkach, w których poprzednia semantyka miałaby większy sens, i należy użyć klasy w przypadkach, w których druga semantyka miałaby większy sens.
Zauważ, że chociaż niektórzy ludzie radzą, że chęć zrobienia czegoś zmiennego jest argumentem przemawiającym za tym, że jest to klasa, sugerowałbym, że jest odwrotnie: jeśli coś, co istnieje w celu przechowywania niektórych danych, będzie podlegać zmianom, i jeśli nie będzie oczywiste, czy instancje są dołączone do czegokolwiek, rzecz powinna być strukturą (prawdopodobnie z odsłoniętymi polami), aby wyjaśnić, że instancje nie są dołączone do niczego innego.
Rozważmy na przykład stwierdzenia:
Osoba somePerson = myPeople.GetPerson („123-45-6789”);
somePerson.Name = "Mary Johnson"; // Był „Mary Smith”
Czy drugie zdanie zmieni informacje w nim przechowywane myPeople
? Jeśli Person
jest strukturą pola narażonego, nie będzie, a fakt, że nie będzie oczywistą konsekwencją bycia strukturą pola narażonego ; jeśli Person
jest strukturą i chce się zaktualizować myPeople
, to oczywiście trzeba coś zrobić myPeople.UpdatePerson("123-45-6789", somePerson)
. Jeśli Person
jest to klasa, może być znacznie trudniej ustalić, czy powyższy kod nigdy nie zaktualizuje zawartości MyPeople
, zawsze ją zaktualizuje, a czasem zaktualizuje.
Jeśli chodzi o pogląd, że struktury powinny być „niezmienne”, ogólnie nie zgadzam się. Istnieją uzasadnione przypadki użycia „niezmiennych” struktur (gdzie niezmienniki są wymuszane w konstruktorze), ale wymaganie, aby cała struktura musiała zostać przepisana za każdym razem, gdy jakakolwiek część jej zmian jest niewygodna, marnotrawna i bardziej skłonna do powodowania błędów niż po prostu ujawnianie pola bezpośrednio. Na przykład rozważmy PhoneNumber
strukturę, której pola obejmują między innymi, AreaCode
i Exchange
, i załóżmy, że ma List<PhoneNumber>
. Następujący efekt powinien być dość wyraźny:
for (int i = 0; i <myList.Count; i ++)
{
PhoneNumber theNumber = myList [i];
if (theNumber.AreaCode == "312")
{
string newExchange = "";
if (new312to708Exchanges.TryGetValue (theNumber.Exchange), out newExchange)
{
theNumber.AreaCode = "708";
theNumber.Exchange = newExchange;
myList [i] = theNumber;
}
}
}
Zauważ, że nic w powyższym kodzie nie wie ani nie troszczy się o żadne pola PhoneNumber
inne niż AreaCode
i Exchange
. Gdyby PhoneNumber
był tak zwaną „niezmienną” strukturą, konieczne byłoby albo dostarczenie withXX
metody dla każdego pola, która zwróciłaby nową instancję struktury, która posiadałaby przekazaną wartość we wskazanym polu, albo byłaby konieczna dla kodu takiego jak wyżej, aby wiedzieć o każdym polu w strukturze. Niezupełnie atrakcyjne.
BTW, istnieją co najmniej dwa przypadki, w których struktury mogą zasadnie zawierać odniesienia do typów zmiennych.
- Semantyka struktury wskazuje, że przechowuje ona tożsamość przedmiotowego obiektu, a nie skrót do przechowywania właściwości obiektu. Na przykład `KeyValuePair` przechowuje tożsamości niektórych przycisków, ale nie oczekuje się, że będą przechowywać trwałe informacje o pozycjach tych przycisków, stanach podświetlenia itp.
- Struktura wie, że posiada jedyne odniesienie do tego obiektu, nikt inny nie otrzyma odniesienia, a wszelkie mutacje, które kiedykolwiek zostaną wykonane na tym obiekcie, zostaną wykonane, zanim odniesienie do niego zostanie zapisane w dowolnym miejscu.
W poprzednim scenariuszu TOŻSAMOŚĆ obiektu będzie niezmienna; w drugim przypadku klasa zagnieżdżonego obiektu może nie wymuszać niezmienności, ale struktura przechowująca odwołanie to zrobi.