Jakiś facet zadał mi to pytanie kilka miesięcy temu i nie mogłem szczegółowo tego wyjaśnić. Jaka jest różnica między typem referencyjnym a typem wartości w C #?
Wiem, że typy wartości są int
, bool
, float
, etc i referencyjne typy delegate
, interface
itp Albo jest to złe, też?
Czy możesz mi to wyjaśnić w profesjonalny sposób?
c#
.net
value-type
reference-type
tugberk
źródło
źródło
Odpowiedzi:
Twoje przykłady są trochę dziwne, ponieważ podczas
int
,bool
ifloat
są specyficzne typy, interfejsów i delegaci są rodzaje typu - tak jakstruct
ienum
są rodzaje typów wartości.Pisałem o wyjaśnienie rodzajów i typów wartości odniesienia w tym artykule . Z przyjemnością omówię wszystkie fragmenty, które mogą być mylące.
Wersja „TL; DR” polega na przemyśleniu wartości zmiennej / wyrażenia określonego typu. W przypadku typu wartości wartością jest sama informacja. W przypadku typu referencyjnego wartość jest odwołaniem, które może być zerowe lub może być sposobem nawigacji do obiektu zawierającego informacje.
Na przykład pomyśl o zmiennej jak o kartce papieru. Mógłby mieć wpisaną wartość „5” lub „false”, ale nie mógł mieć mojego domu… musiałby mieć wskazówki dojazdu do mojego domu. Te kierunki są odpowiednikiem odniesienia. W szczególności dwie osoby mogą mieć różne kartki papieru zawierające te same wskazówki do mojego domu - a jeśli jedna osoba postąpiłaby zgodnie z tymi wskazówkami i pomalowała mój dom na czerwono, druga osoba również zauważyłaby tę zmianę. Gdyby obaj mieli po prostu oddzielne zdjęcia mojego domu na papierze, jedna osoba kolorująca papier w ogóle nie zmieniłaby papieru drugiej osoby.
źródło
while int, bool and float are specific types, interfaces and delegates are kinds of type - just like struct and enum are kinds of value types
. Co masz na myśli, mówiąc int, bool to określone typy? Wszystko w C # np. Int, bool, float, class, interface, delegate jest typem (dokładniej typem danych). Typy danych są segregowane jako „Typ odwołania” i „Typ wartości” w języku C #. W takim razie dlaczego mówisz, że int jest określonym typem, a interfejs jest rodzajem typu?int
jest strukturą,string
jest klasą,Action
jest delegatem itp. Twoja lista „int, bool, float, class, interface, delegate” jest listą zawierającą różne rodzaje rzeczy, tak samo jak „10, int” jest lista zawierająca różne rodzaje rzeczy.Typ wartości:
Przechowuje jakąś wartość, a nie adresy pamięci
Przykład:
Struct
Przechowywanie:
TL; DR : Wartość zmiennej jest przechowywana wszędzie tam, gdzie jest ona zdeklarowana. Na przykład zmienne lokalne znajdują się na stosie, ale zadeklarowane wewnątrz klasy jako element członkowski, pozostają na stercie ściśle powiązane z klasą, w której są zadeklarowane.
Dłuższe : w ten sposób typy wartości są przechowywane wszędzie tam, gdzie są zadeklarowane. Np .:
int
wartość a wewnątrz funkcji jako zmienna lokalna byłaby przechowywana na stosie, podczas gdyint
wartość in zadeklarowana jako składowa w klasie byłaby przechowywana na stercie z klasą, w której jest zadeklarowana. Typ wartości na klasa ma typ życia, który jest dokładnie taki sam, jak klasa, w której została zadeklarowana, i nie wymaga prawie żadnej pracy od czyszczenia pamięci. Jest to jednak bardziej skomplikowane, odniosę się do książki @ JonSkeet „ C # In Depth ”Pamięć w .NET ”dla bardziej zwięzłego wyjaśnienia.Zalety:
Typ wartości nie wymaga dodatkowego wyrzucania elementów bezużytecznych. Pobiera śmieci zbierane wraz z instancją, w której żyje. Zmienne lokalne w metodach są czyszczone po opuszczeniu metody.
Wady:
Gdy do metody przekazywany jest duży zestaw wartości, zmienna odbierająca faktycznie kopiuje, więc w pamięci znajdują się dwie nadmiarowe wartości.
Ponieważ zajęcia są pomijane, traci wszystkie korzyści
Typ odniesienia:
Przechowuje adres pamięci o wartości, a nie wartości
Przykład:
Klasa
Przechowywanie:
Przechowywane na stercie
Zalety:
Kiedy przekazujesz zmienną referencyjną do metody i zmienia się ona faktycznie zmienia oryginalną wartość, podczas gdy w typach wartości pobierana jest kopia danej zmiennej i ta wartość jest zmieniana.
Gdy wielkość zmiennej jest większa, typ odniesienia jest dobry
Ponieważ klasy są zmiennymi typu referencyjnego, dają możliwość ponownego wykorzystania, co jest korzystne dla programowania obiektowego
Wady:
Więcej pracy przy alokowaniu i usuwaniu odwołań podczas odczytywania przeciążenia value.extra dla modułu odśmiecania pamięci
źródło
Okazało się, że łatwiej jest zrozumieć różnicę tych dwóch, jeśli wiesz, jak komputer alokuje rzeczy w pamięci i wiesz, czym jest wskaźnik.
Odniesienie jest zwykle powiązane ze wskaźnikiem. Oznacza to, że adres pamięci, w którym znajduje się twoja zmienna, faktycznie zawiera inny adres pamięci rzeczywistego obiektu w innej lokalizacji pamięci.
Przykład, który zamierzam podać, jest znacznie uproszczony, więc potraktuj go z przymrużeniem oka.
Wyobraź sobie, że pamięć komputera to zbiór skrytek pocztowych w rzędzie (zaczynając od PO Box 0001 do PO Box n), które mogą pomieścić coś w środku. Jeśli skrzynki pocztowe nie robią tego za Ciebie, wypróbuj tablicę haszującą lub słownik, tablicę lub coś podobnego.
Tak więc, gdy robisz coś takiego:
var a = "Witaj";
komputer wykona następujące czynności:
Więc to trochę jak alias (0500 to a).
Typ wartości będzie przechowywać rzeczywisty element w swojej lokalizacji pamięci.
Tak więc, gdy robisz coś takiego:
var a = 1;
komputer wykona następujące czynności:
Zauważ, że nie przydzielamy dodatkowej pamięci do przechowywania rzeczywistej wartości (1). Zatem a faktycznie przechowuje rzeczywistą wartość i dlatego nazywa się typem wartości.
źródło
To jest z mojego postu z innego forum, jakieś dwa lata temu. Chociaż językiem jest vb.net (w przeciwieństwie do C #), koncepcje typu wartości w porównaniu z typem referencyjnym są jednolite w całej sieci .NET, a przykłady nadal są aktualne.
Należy również pamiętać, że w .net WSZYSTKIE typy technicznie wywodzą się z typu podstawowego Object. Typy wartości są zaprojektowane tak, aby zachowywały się jako takie, ale ostatecznie dziedziczą również funkcjonalność typu podstawowego Object.
A. Typy wartości są po prostu tym - reprezentują odrębny obszar w pamięci, w którym przechowywana jest dyskretna WARTOŚĆ. Typy wartości mają stałą wielkość pamięci i są przechowywane w stosie, który jest zbiorem adresów o stałym rozmiarze.
Kiedy składasz takie oświadczenie:
Wykonałeś następujące czynności:
Wartość każdej zmiennej istnieje dyskretnie w każdej lokalizacji pamięci.
B. Typy referencyjne mogą mieć różne rozmiary. Dlatego nie można ich przechowywać w „stosie” (pamiętaj, że stos jest zbiorem alokacji pamięci o stałym rozmiarze?). Są one przechowywane w „Zarządzanym stosie”. Wskaźniki (lub „odwołania”) do każdego elementu na zarządzanej stercie są utrzymywane na stosie (podobnie jak adres). Twój kod używa tych wskaźników w stosie, aby uzyskać dostęp do obiektów przechowywanych w zarządzanej stercie. Więc kiedy twój kod używa zmiennej referencyjnej, w rzeczywistości używa wskaźnika (lub „adresu” do lokalizacji pamięci w zarządzanej stercie).
Powiedzmy, że utworzyłeś klasę o nazwie clsPerson z ciągiem Property Person.Name
W takim przypadku, gdy składasz takie oświadczenie:
W powyższym przypadku właściwość p1.Name zwróci „Jim Morrison”, jak można się spodziewać. Właściwość p2.Name zwróci RÓWNIEŻ „Jim Morrison”, zgodnie z intuicyjnym oczekiwaniem. Uważam, że zarówno p1, jak i p2 reprezentują różne adresy w stosie. Jednak teraz, gdy przypisałeś p2 wartość p1, zarówno p1, jak i p2 wskazują na SAMEJ LOKALIZACJĘ na zarządzanym stercie.
Teraz rozważ tę sytuację:
W tej sytuacji utworzyłeś jedną nową instancję klasy osoby na stercie zarządzanym ze wskaźnikiem p1 na stosie, który odwołuje się do obiektu, i ponownie przypisałeś właściwości nazwy instancji obiektu wartość „Jim Morrison”. Następnie utworzyłeś inny wskaźnik p2 w stosie i wskazałeś go na ten sam adres na zarządzanym stercie, jak ten, do którego odwołuje się p1 (kiedy wykonałeś przypisanie p2 = p1).
Nadchodzi zwrot akcji. Kiedy przypiszesz właściwość nazwy p2 wartość "Janis Joplin", zmieniasz właściwość Nazwa dla obiektu REFERENCED zarówno przez p1, jak i p2, tak że jeśli uruchomisz następujący kod:
Czy to ma sens?
Ostatni, ubiegły, zeszły. Jeśli to zrobisz:
Masz teraz dwa różne obiekty Person. Jednak w chwili, gdy zrobisz TO ponownie:
Skierowałeś teraz oba z powrotem do „Jima Morrisona”. (Nie jestem do końca pewien, co się stało z obiektem na stercie, do którego odwołuje się p2.. MYŚLĘ, że teraz wyszedł poza zakres. Jest to jeden z tych obszarów, gdzie mam nadzieję, że ktoś może mnie wyprostować ...). -EDIT: WIERZĘ, że właśnie dlatego przed wykonaniem nowego przypisania należy ustawić p2 = Nothing LUB p2 = New clsPerson.
Jeszcze raz, jeśli teraz zrobisz TO:
Oba msgBoxes będą teraz zwracać „Jimi Hendrix”
Może to być trochę zagmatwane i powiem ostatni raz, że niektóre szczegóły mogą być błędne.
Powodzenia i miejmy nadzieję, że inni, którzy wiedzą lepiej ode mnie, pomogą w wyjaśnieniu niektórych z tego. . .
źródło
wartość typ danych i typ danych odniesienia
1) wartość (zawiera dane bezpośrednio), ale odniesienie (odnosi się do danych)
2) w wartości (każda zmienna ma swoją własną kopię), ale
w odniesieniu (więcej niż zmienna może odnosić się do niektórych obiektów)
3) w wartości (zmienna operacyjna nie może wpływać na inną zmienną), ale w referencji (zmienna może wpływać na inne)
4) typy wartości to (int, bool, float), ale typ referencyjny to (tablica, obiekty klas, string)
źródło
Typ wartości:
Stały rozmiar pamięci.
Przechowywane w pamięci stosu.
Zawiera aktualną wartość.
Dawny. int, char, bool itp ...
Typ odniesienia:
Pamięć nieustalona.
Przechowywane w pamięci sterty.
Przechowuje adres pamięci aktualnej wartości.
Dawny. ciąg, tablica, klasa itp ...
źródło
„Zmienne oparte na typach wartości bezpośrednio zawierają wartości. Przypisanie jednej zmiennej typu wartości do innej powoduje skopiowanie zawartej wartości. Różni się to od przypisania zmiennych typu referencyjnego, które kopiuje odniesienie do obiektu, ale nie sam obiekt”. z biblioteki Microsoft.
Pełniejszą odpowiedź można znaleźć tutaj i tutaj .
źródło
Czasami wyjaśnienia nie pomogą, zwłaszcza początkującym. Możesz wyobrazić sobie typ wartości jako plik danych, a typ odniesienia jako skrót do pliku.
Więc jeśli kopiujesz zmienną referencyjną, kopiujesz tylko link / wskaźnik do rzeczywistych danych gdzieś w pamięci. Jeśli skopiujesz typ wartości, naprawdę klonujesz dane w pamięci.
źródło
Jest to prawdopodobnie błędne w ezoteryczny sposób, ale, aby to uprościć:
Typy wartości to wartości, które są przekazywane normalnie „według wartości” (a więc ich kopiowanie). Typy referencyjne są przekazywane „przez referencję” (dając w ten sposób wskaźnik do oryginalnej wartości). Standard .NET ECMA nie gwarantuje, gdzie te „rzeczy” są zapisywane. Możesz zbudować implementację .NET bez stosu lub bez stosu (druga byłaby bardzo złożona, ale prawdopodobnie można by było, używając włókien i wielu stosów)
Struktury są typami wartości (int, bool ... są strukturami lub przynajmniej są symulowane jako ...), klasy są typami referencyjnymi.
Typy wartości pochodzą od System.ValueType. Typ odwołania pochodzi od System.Object.
Teraz ... Na końcu masz typ wartości, „obiekty, do których istnieją odniesienia” i referencje (w C ++ byłyby one nazywane wskaźnikami do obiektów. W .NET są nieprzezroczyste. Nie wiemy, czym one są. Z naszego punktu widzenia one są „uchwytami” obiektu). Te ostatnie są podobne do typów wartości (są przekazywane przez kopię). Tak więc obiekt składa się z obiektu (typu referencyjnego) i zera lub większej liczby odniesień do niego (które są podobne do typów wartości). Gdy nie ma żadnych odniesień, GC prawdopodobnie je zbierze.
Ogólnie (w "domyślnej" implementacji .NET), typ wartości może trafić na stos (jeśli są polami lokalnymi) lub na stercie (jeśli są polami klasy, jeśli są zmiennymi w funkcji iteratora, jeśli są zmiennymi, do których odwołuje się zamknięcie, jeśli są zmiennymi w funkcji asynchronicznej (przy użyciu nowszego Async CTP) ...). Wartość, do której odwołuje się odwołanie, może trafić tylko do stosu. Odniesienia używają tych samych reguł, co typy wartości.
W przypadkach typu wartości, które trafiają na stertę, ponieważ znajdują się w funkcji iteratora, funkcji asynchronicznej lub odwołuje się do nich zamknięcie, jeśli obejrzysz skompilowany plik, zobaczysz, że kompilator utworzył klasę, aby umieścić te zmienne , a klasa jest budowana, gdy wywołujesz funkcję.
Teraz nie wiem, jak pisać długie rzeczy i mam lepsze rzeczy do zrobienia w moim życiu. Jeśli chcesz uzyskać „dokładną”, „akademicką”, „poprawną” wersję, przeczytaj TEN:
http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx
To 15 minut, szukam tego! Jest lepszy niż wersje msdn, ponieważ jest to skondensowany artykuł „gotowy do użycia”.
źródło
Najprostszym sposobem myślenia o typach referencyjnych jest traktowanie ich jako „identyfikatorów obiektów”; jedyne, co można zrobić z identyfikatorem obiektu, to utworzyć jeden, skopiować jeden, zapytać o typ jednego lub zmodyfikować go lub porównać dwa pod kątem równości. Próba zrobienia czegokolwiek innego z identyfikatorem obiektu będzie traktowana jako skrót do wykonania wskazanej akcji z obiektem, do którego odwołuje się ten identyfikator.
Załóżmy, że mam dwie zmienne X i Y typu Car - typ referencyjny. Tak się składa, że Y zawiera „ID obiektu nr 19531”. Jeśli powiem „X = Y”, spowoduje to, że X zatrzyma „ID obiektu nr 19531”. Zwróć uwagę, że ani X, ani Y nie posiadają samochodu. Samochód, znany również jako „identyfikator obiektu nr 19531”, jest przechowywany w innym miejscu. Kiedy skopiowałem Y do X, wszystko, co zrobiłem, to skopiowanie numeru identyfikacyjnego. Teraz przypuśćmy, że X.Color = Colors.Blue. Taka instrukcja będzie traktowana jako instrukcja, aby znaleźć „obiekt o numerze ID 19531” i pomalować go na niebiesko. Zauważ, że chociaż X i Y odnoszą się teraz do niebieskiego samochodu, a nie żółtego, to stwierdzenie nie wpływa w rzeczywistości na X ani Y, ponieważ oba nadal odnoszą się do „ID obiektu nr 19531”, który nadal jest tym samym samochodem. zawsze był.
źródło
Typy zmiennych i wartość referencyjna są łatwe do zastosowania i dobrze zastosowane w modelu domeny, ułatwiają proces tworzenia.
Aby usunąć jakikolwiek mit dotyczący ilości „typu wartości”, skomentuję, jak jest to obsługiwane na platformie. NET, w szczególności w C # (CSharp), gdy wywoływana jest APIS i wysyłaj parametry według wartości, przez odwołanie, w naszych metodach i funkcjach oraz jak prawidłowo traktować fragmenty tych wartości.
Przeczytaj ten artykuł Wartość i odwołanie typu zmiennej w języku C #
źródło
Załóżmy, że
v
jest to wyrażenie / zmienna typu wartości i wyrażenie / zmiennar
typu referencyjnegoZatem zmienna typu wartości przechowuje rzeczywistą wartość (5 lub „h”). Zmienna typu referencyjnego przechowuje tylko odsyłacz do pola metaforycznego, w którym znajduje się wartość.
źródło
Przed wyjaśnieniem różnych typów danych dostępnych w C #, ważne jest, aby wspomnieć, że C # jest językiem o jednoznacznie określonym typie. Oznacza to, że każda zmienna, stała, parametr wejściowy, typ zwracany i ogólnie każde wyrażenie, którego wynikiem jest wartość, ma typ.
Każdy typ zawiera informacje, które zostaną osadzone przez kompilator w pliku wykonywalnym jako metadane, które będą używane przez środowisko uruchomieniowe języka wspólnego (CLR), aby zagwarantować bezpieczeństwo typów podczas przydzielania i odzyskiwania pamięci.
Jeśli chcesz wiedzieć, ile pamięci przydziela określony typ, możesz użyć operatora sizeof w następujący sposób:
Dane wyjściowe pokażą liczbę bajtów przydzielonych przez każdą zmienną.
Informacje związane z każdym typem to:
Elementy członkowskie (metody, pola, zdarzenia itp.) Zawarte w typie. Na przykład, jeśli sprawdzimy definicję typu int, znajdziemy następującą strukturę i składowe:
Zarządzanie pamięcią Kiedy w systemie operacyjnym działa wiele procesów, a ilość pamięci RAM nie jest wystarczająca, aby wszystko pomieścić, system operacyjny mapuje części dysku twardego z pamięcią RAM i rozpoczyna przechowywanie danych na dysku twardym. System operacyjny użyje niż określone tabele, w których adresy wirtualne są mapowane na odpowiadające im adresy fizyczne w celu wykonania żądania. Ta możliwość zarządzania pamięcią nazywana jest pamięcią wirtualną.
W każdym procesie dostępna pamięć wirtualna jest zorganizowana w następujących 6 sekcjach, ale ze względu na znaczenie tego tematu skupimy się tylko na stosie i stercie.
Stos Stos jest strukturą danych LIFO (ostatnie weszło, pierwsze wyszło), z wielkością zależną od systemu operacyjnego (domyślnie dla maszyn ARM, x86 i x64 system Windows rezerwuje 1 MB, podczas gdy Linux rezerwuje od 2 MB do 8 MB w zależności od wersja).
Ta sekcja pamięci jest automatycznie zarządzana przez procesor. Za każdym razem, gdy funkcja deklaruje nową zmienną, kompilator przydziela na stosie nowy blok pamięci o wielkości odpowiadającej jej rozmiarowi, a po zakończeniu funkcji blok pamięci dla zmiennej jest zwalniany.
Sterta Ten region pamięci nie jest zarządzany automatycznie przez procesor, a jego rozmiar jest większy niż stos. Po wywołaniu słowa kluczowego new kompilator zaczyna szukać pierwszego wolnego bloku pamięci, który pasuje do rozmiaru żądania. a kiedy go znajdzie, jest oznaczany jako zarezerwowany za pomocą wbudowanej funkcji malloc () w języku C i zwraca wskaźnik do tej lokalizacji. Możliwe jest również zwolnienie bloku pamięci za pomocą wbudowanej funkcji free () w języku C. Mechanizm ten powoduje fragmentację pamięci i musi używać wskaźników, aby uzyskać dostęp do odpowiedniego bloku pamięci, jest wolniejszy niż stos, aby wykonać operacje odczytu / zapisu.
Typy niestandardowe i wbudowane Podczas gdy C # zapewnia standardowy zestaw wbudowanych typów reprezentujących liczby całkowite, wartości logiczne, znaki tekstowe i tak dalej, możesz użyć konstrukcji, takich jak struct, class, interface i enum, aby utworzyć własne typy.
Przykład niestandardowego typu używającego konstrukcji struct:
Typy wartości i odwołań Możemy podzielić typ C # na następujące kategorie:
Typy wartości Typy wartości pochodzą z klasy System.ValueType, a zmienne tego typu zawierają swoje wartości w ramach alokacji pamięci na stosie. Dwie kategorie typów wartości to struct i enum.
Poniższy przykład przedstawia element członkowski typu boolean. Jak widać, nie ma wyraźnego odwołania do klasy System.ValueType, dzieje się tak, ponieważ ta klasa jest dziedziczona przez strukturę.
Typy odwołań Z drugiej strony, typy odwołań nie zawierają rzeczywistych danych przechowywanych w zmiennej, ale adres pamięci sterty, w której przechowywana jest wartość. Kategorie typów odwołań to klasy, delegaci, tablice i interfejsy.
W czasie wykonywania, gdy deklarowana jest zmienna typu referencyjnego, zawiera ona wartość null do momentu przypisania do niej obiektu, który został utworzony przy użyciu słów kluczowych new.
Poniższy przykład przedstawia elementy członkowskie List typu ogólnego.
Jeśli chcesz znaleźć adres pamięci konkretnego obiektu, klasa System.Runtime.InteropServices zapewnia dostęp do zarządzanych obiektów z niezarządzanej pamięci. W poniższym przykładzie użyjemy statycznej metody GCHandle.Alloc () do przydzielenia uchwytu do łańcucha, a następnie metody AddrOfPinnedObject w celu pobrania jego adresu.
Wynik będzie
Referencje Oficjalna dokumentacja: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019
źródło
Istnieje wiele drobnych szczegółów różnic między typami wartości i typami referencyjnymi, które są wyraźnie określone w normie, a niektóre z nich nie są łatwe do zrozumienia, szczególnie dla początkujących.
Zobacz standard ECMA 33, Common Language Infrastructure (CLI) . Interfejs CLI jest również znormalizowany przez ISO. Podałbym odniesienie, ale w przypadku ECMA musimy pobrać plik PDF, a ten link zależy od numeru wersji. Normy ISO kosztują.
Jedna różnica polega na tym, że typy wartości można umieszczać w ramkach, ale zazwyczaj nie można ich umieszczać w typach odwołań. Są wyjątki, ale są one dość techniczne.
Typy wartości nie mogą mieć konstruktorów instancji bez parametrów ani finalizatorów i nie mogą odwoływać się do siebie. Odnoszenie się do siebie oznacza na przykład, że jeśli istnieje typ wartości Node, wówczas członkiem Node nie może być węzłem . Myślę, że w specyfikacjach są inne wymagania / ograniczenia, ale jeśli tak, to nie są one zebrane w jednym miejscu.
źródło