W .NET istnieją dwie kategorie typów, typy referencyjne i typy wartości .
Struktury są typami wartości, a klasy są typami referencyjnymi .
Ogólna różnica polega na tym, że typ odniesienia znajduje się na stercie, a typ wartości - w wierszu, to znaczy tam, gdzie jest zdefiniowana zmienna lub pole.
Zmienna zawierająca typ wartości zawiera całą wartość typu wartości. W przypadku struct oznacza to, że zmienna zawiera całą strukturę wraz ze wszystkimi jej polami.
Zmienna zawierająca typ odwołania zawiera wskaźnik lub odwołanie do innego miejsca w pamięci, w którym znajduje się rzeczywista wartość.
Ma to jedną zaletę, na początek:
- typy wartości zawsze zawierają wartość
- typy referencyjne mogą zawierać wartości null -reference, co oznacza, że nie odnoszą się one do niczego w ogóle w tej chwili
Wewnętrznie typy referencyjne są implementowane jako wskaźniki, a wiedząc, że i wiedząc, jak działa przypisywanie zmiennych, istnieją inne wzorce zachowań:
- kopiując zawartość zmiennej typu wartości do innej zmiennej, kopiuje całą zawartość do nowej zmiennej, rozróżniając obie. Innymi słowy, po skopiowaniu zmiany w jednym nie wpłyną na drugie
- kopiowanie zawartości zmiennej typu odwołania do innej zmiennej powoduje skopiowanie odwołania, co oznacza, że masz teraz dwa odniesienia do tego samego, gdzieś indziej, przechowywania rzeczywistych danych. Innymi słowy, po skopiowaniu zmiana danych w jednym odnośniku będzie również miała wpływ na drugi, ale tylko dlatego, że tak naprawdę patrzysz na te same dane w obu miejscach
Kiedy deklarujesz zmienne lub pola, oto jak różnią się dwa typy:
- zmienna: typ wartości żyje na stosie, typ referencyjny żyje na stosie jako wskaźnik do miejsca w pamięci stosu, w którym żyje rzeczywista pamięć (choć należy zauważyć, że seria artykułów Erica Lippertsa: Stos jest szczegółem implementacji ).
- class / struct-field: typ wartości żyje całkowicie wewnątrz typu, typ referencyjny mieszka wewnątrz typu jako wskaźnik do miejsca w pamięci sterty, w którym znajduje się rzeczywista pamięć.
Krótkie podsumowanie każdego:
Tylko klasy:
Tylko konstrukcje:
Zarówno klasy, jak i struktury:
źródło
c# struct memory overhead
i znalazłem odpowiedź Hansa Passanta, która mówi, że nie, to też nie jest przypadek. Więc co ma pan na myśli?class
zarządzana jest pamięć (obsługiwana przez moduł odśmiecający), podczas gdy instancjestruct
nie są .W .NET deklaracje struct i class rozróżniają typy referencyjne i typy wartości.
Kiedy omijasz typ referencyjny, jest tylko jeden faktycznie zapisany. Cały kod, który uzyskuje dostęp do instancji, ma dostęp do tego samego.
Gdy podasz typ wartości, każdy z nich jest kopią. Cały kod działa na własnej kopii.
Można to pokazać na przykładzie:
Dla klasy byłoby inaczej
Klasy mogą być niczym - odwołanie może wskazywać na zero.
Struktury są rzeczywistą wartością - mogą być puste, ale nigdy zerowe. Z tego powodu struktury zawsze mają domyślny konstruktor bez parametrów - potrzebują „wartości początkowej”.
źródło
Różnica między strukturami i klasami:
źródło
Od wyboru między klasą a strukturą Microsoftu ...
źródło
Oprócz wszystkich różnic opisanych w innych odpowiedziach:
Jeśli szukasz filmu wyjaśniającego wszystkie różnice, możesz zapoznać się z częścią 29 - Kurs C # - Różnica między klasami a strukturami w języku C # .
źródło
Wystąpienia klas są przechowywane na zarządzanej stercie. Wszystkie zmienne „zawierające” instancję są po prostu odwołaniem do instancji na stercie. Przekazanie obiektu do metody powoduje przekazanie kopii odwołania, a nie samego obiektu.
Struktury (technicznie, typy wartości) są przechowywane wszędzie tam, gdzie są używane, podobnie jak typ pierwotny. Zawartość może być kopiowana przez środowisko wykonawcze w dowolnym momencie i bez wywoływania dostosowanego konstruktora kopii. Przekazanie typu wartości do metody wymaga skopiowania całej wartości, ponownie bez wywoływania dostosowywanego kodu.
Rozróżnienie jest poprawione przez nazwy C ++ / CLI: „klasa referencyjna” jest klasą, jak opisano najpierw, „klasa wartości” jest klasą opisaną jako druga. Słowa kluczowe „class” i „struct” używane w języku C # są po prostu czymś, czego należy się nauczyć.
źródło
źródło
Struktura a klasa
Struktura jest typem wartości, więc jest przechowywana na stosie, ale klasa jest typem referencyjnym i jest przechowywana na stercie.
Struktura nie obsługuje dziedziczenia i polimorfizmu, ale klasa obsługuje oba te elementy.
Domyślnie wszyscy członkowie struktury są publiczni, ale członkowie klasy mają z natury charakter prywatny.
Ponieważ struktura jest typem wartości, nie możemy przypisać wartości null do obiektu struct, ale nie jest tak w przypadku klasy.
źródło
Aby dodać do innych odpowiedzi, należy zwrócić uwagę na jedną zasadniczą różnicę, a mianowicie sposób przechowywania danych w tablicach, ponieważ może to mieć duży wpływ na wydajność.
Tak więc tablica struktur wygląda tak w pamięci
[struct][struct][struct][struct][struct][struct][struct][struct]
Natomiast tablica klas wygląda tak
[pointer][pointer][pointer][pointer][pointer][pointer][pointer][pointer]
W tablicy klas wartości, które Cię interesują, nie są przechowywane w tablicy, ale w innej pamięci.
W zdecydowanej większości aplikacji różnica ta nie ma tak naprawdę znaczenia, jednak w kodzie o wysokiej wydajności wpłynie to na lokalizację danych w pamięci i będzie miało duży wpływ na wydajność pamięci podręcznej procesora. Używanie klas, gdy można / należy użyć struktur, znacznie zwiększy liczbę braków pamięci podręcznej na procesorze.
Najwolniejszą rzeczą, jaką robi współczesny procesor, nie jest niszczenie liczb, pobieranie danych z pamięci, a trafienie w pamięć podręczną L1 jest wielokrotnie szybsze niż odczyt danych z pamięci RAM.
Oto kod, który możesz przetestować. Na moim komputerze iteracja po tablicy klas zajmuje ~ 3 razy dłużej niż tablica struct.
źródło
Aby go ukończyć, istnieje inna różnica w korzystaniu z
Equals
metody, która jest dziedziczona przez wszystkie klasy i struktury.Powiedzmy, że mamy klasę i strukturę:
a w metodzie Main mamy 4 obiekty.
Następnie:
Tak więc struktury są odpowiednie dla obiektów numerycznych, takich jak punkty (zapisz współrzędne xiy). A zajęcia są odpowiednie dla innych. Nawet jeśli 2 osoby mają to samo imię, wzrost, wagę ... to nadal są 2 osoby.
źródło
Cóż, na początek, struktura jest przekazywana raczej przez wartość niż przez referencję. Struktury są dobre dla stosunkowo prostych struktur danych, podczas gdy klasy mają znacznie większą elastyczność z architektonicznego punktu widzenia dzięki polimorfizmowi i dziedziczeniu.
Inni prawdopodobnie zapewnią ci więcej szczegółów niż ja, ale używam struktur, gdy struktura, do której dążę, jest prosta.
źródło
Oprócz podstawowej różnicy między specyfikatorem dostępu i kilkoma wymienionymi powyżej, chciałbym dodać niektóre z głównych różnic, w tym kilka wspomnianych powyżej, z próbką kodu z wyjściem, która da wyraźniejszy obraz odniesienia i wartości
Struktury:
Klasa:
Próbka kodu
Wynik
Początkowa wartość obiektu konstrukcyjnego to: 10
Metoda konstrukcji wewnętrznej Wartość metody wewnętrznej obiektu konstrukcyjnego wynosi: 20
Po wywołaniu metody wartość obiektu konstrukcyjnego wynosi: 10
Początkowa wartość obiektu klasy to: 10
Metoda klasy wewnętrznej Wartość metody wewnętrznej obiektu klasy wynosi: 20
Po wywołaniu metody wartość obiektu klasy wynosi: 20
Tutaj możesz wyraźnie zobaczyć różnicę między połączeniem według wartości a wywołaniem przez odniesienie.
źródło
Zdarzenia zadeklarowane w klasie mają swój dostęp + = i - = automatycznie blokowane przez blokadę (this), aby były bezpieczne dla wątków (zdarzenia statyczne są blokowane na typie klasy). Zdarzenia zadeklarowane w strukturze nie mają automatycznie blokowanego dostępu + = i - =. Blokada (this) dla struktury nie działałaby, ponieważ można zablokować tylko wyrażenie typu referencyjnego.
Utworzenie instancji struct nie może spowodować wyrzucania elementów bezużytecznych (chyba że konstruktor bezpośrednio lub pośrednio utworzy instancję typu odwołania), natomiast utworzenie instancji typu odniesienia może spowodować odśmiecanie.
Struktura zawsze ma wbudowany publiczny konstruktor domyślny.
Oznacza to, że struktura jest zawsze możliwa do utworzenia, podczas gdy klasa może nie być, ponieważ wszystkie jej konstruktory mogą być prywatne.
Struktura nie może mieć destruktora. Destruktor to po prostu przesłonięcie obiektu. Finalizacja w przebraniu, a struktury, będące typami wartości, nie podlegają odśmiecaniu.
Struktura jest domyślnie zapieczętowana, klasa nie.
Struktura nie może być abstrakcyjna, klasa może.
Struktur nie może wywołać: base () w swoim konstruktorze, podczas gdy klasa bez wyraźnej klasy bazowej może.
Struktura nie może rozszerzyć innej klasy, klasa może.
Struktura nie może deklarować chronionych elementów (na przykład pól, typów zagnieżdżonych), które klasa może.
Struktura nie może deklarować elementów funkcji abstrakcyjnych, klasa abstrakcyjna może.
Struktura nie może deklarować elementów funkcji wirtualnej, klasa może.
Struktura nie może deklarować zapieczętowanych elementów funkcji, klasa może.
Struktura nie może zadeklarować elementów funkcji zastępowania, klasa może to zrobić.
Jedynym wyjątkiem od tej reguły jest to, że struct może zastąpić wirtualne metody System.Object, viz, Equals () i GetHashCode () oraz ToString ().
źródło
Object
, które zawierałoby odniesienie do pudełkowej kopii struktury.Jak wspomniano wcześniej: klasy są typem odniesienia, natomiast struktury są typami wartości ze wszystkimi konsekwencjami.
Zasadniczo Wytyczne projektowania ramowego zalecają stosowanie Struktur zamiast klas, jeśli:
źródło
Jest jeden interesujący przypadek układanki „klasa kontra struktura” - sytuacja, w której musisz zwrócić kilka wyników z metody: wybierz, której użyć. Jeśli znasz historię ValueTuple - wiesz, że dodano ValueTuple (struct), ponieważ powinna być bardziej skuteczna niż Tuple (klasa). Ale co to oznacza w liczbach? Dwa testy: jeden to struct / klasa, która ma 2 pola, drugi z struct / class, które mają 8 pól (o wymiarze większym niż 4 - klasa powinna stać się bardziej efektywna niż struct pod względem tików procesora, ale oczywiście należy również wziąć pod uwagę obciążenie GC ).
PS Kolejnym punktem odniesienia dla konkretnego przypadku „solidna lub klasa ze zbiorami” jest: https://stackoverflow.com/a/45276657/506147
Test kodu:
źródło
To prawda, należy jednak pamiętać, że od .NET 2 struktury obsługują wersję Nullable, a C # dostarcza trochę cukru syntaktycznego, aby było łatwiejsze w użyciu.
źródło
(object)(default(int?)) == null
że nie możesz zrobić z żadnym innym rodzajem wartości, ponieważ dzieje się tutaj coś więcej niż tylko cukier. Jedyny cukier jestint?
dlaNullable<int>
.Każda zmienna lub pole pierwotnego typu wartości lub typu struktury zawiera unikalną instancję tego typu, w tym wszystkie jego pola (publiczne i prywatne). Natomiast zmienne lub pola typów odwołań mogą mieć wartość null lub mogą odnosić się do obiektu przechowywanego w innym miejscu, do którego może istnieć dowolna liczba innych odwołań. Pola struktury będą przechowywane w tym samym miejscu, co zmienna lub pole tego typu struktury, które mogą znajdować się na stosie lub mogą być częścią innego obiektu stosu.
Utworzenie zmiennej lub pola o pierwotnym typie wartości spowoduje utworzenie go z wartością domyślną; utworzenie zmiennej lub pola typu struktury spowoduje utworzenie nowej instancji, tworząc wszystkie pola w niej w sposób domyślny. Utworzenie nowej instancji typu odwołania rozpocznie się od utworzenia wszystkich pól w niej w sposób domyślny, a następnie uruchomienia opcjonalnego dodatkowego kodu w zależności od typu.
Skopiowanie jednej zmiennej lub pola typu pierwotnego do innej spowoduje skopiowanie wartości. Skopiowanie jednej zmiennej lub pola typu struktury do drugiej spowoduje skopiowanie wszystkich pól (publicznych i prywatnych) z pierwszej instancji do drugiej. Skopiowanie jednej zmiennej lub pola typu odniesienia do innego spowoduje, że ta ostatnia będzie odnosić się do tej samej instancji co pierwsza (jeśli istnieje).
Należy zauważyć, że w niektórych językach, takich jak C ++, zachowanie semantyczne typu jest niezależne od sposobu jego przechowywania, ale nie jest to prawdą w przypadku .NET. Jeśli typ implementuje semantykę wartości zmiennej, kopiowanie jednej zmiennej tego typu do innej kopiuje właściwości pierwszej do innej instancji, do której odwołuje się druga, i użycie elementu drugiej mutacji spowoduje zmianę tej drugiej instancji , ale nie pierwszy. Jeśli typ implementuje zmienną semantykę odniesienia, skopiowanie jednej zmiennej do drugiej i użycie elementu drugiej do mutacji obiektu wpłynie na obiekt, do którego odnosi się pierwsza zmienna; typy z niezmienną semantyką nie pozwalają na mutację, więc nie ma znaczenia semantycznego, czy kopiowanie tworzy nową instancję, czy tworzy kolejne odwołanie do pierwszej.
W .NET typy wartości mogą implementować dowolną z powyższych semantyek, pod warunkiem, że wszystkie ich pola mogą zrobić podobnie. Typ referencyjny może jednak implementować tylko semantykę zmienną referencyjną lub semantykę niezmienną; typy wartości z polami zmiennych typów zmiennych są ograniczone do implementacji semantyki zmiennych zmiennych lub dziwnej hybrydowej semantyki.
źródło