Was ist der Unterschied zwischen einem Referenztyp und einem Werttyp in c #?


100

Ein Typ hat mir diese Frage vor ein paar Monaten gestellt und ich konnte sie nicht im Detail erklären. Was ist der Unterschied zwischen einem Referenztyp und einem Werttyp in C #?

Ich weiß , dass Werttypen sind int, bool, float, etc. und Referenztypen sind delegate, interfaceusw. Oder ist dies falsch, auch?

Können Sie es mir professionell erklären?


3
Als kleine Anmerkung denke ich, dass die Frage nach C # gestellt wird, aber in Wirklichkeit geht es um C # + .NET. Sie können C # nicht analysieren, ohne .NET zu analysieren. Ich werde die Frage nicht wiederholen, da es einige Punkte geben könnte, die man analysieren muss, ohne die andere zu analysieren (Iteratoren und Abschlüsse, ich sehe dich an)
xanatos

@xanatos ist am besten eine Frage zur CLI, die C #, VB.Net und Net gemeinsam haben. Es sollte ein Tag für CLI geben, aber CLI wird für etwas anderes verwendet. Es gibt CLR, aber das ist eine Implementierung, kein Standard.
user34660

Antworten:


172

Ihre Beispiele sind ein wenig seltsam , weil während int, boolund floatsind bestimmte Typen, Schnittstellen und Delegierten sind Arten von Typ - wie structund enumsind Arten von Werttypen.

Ich habe in diesem Artikel eine Erläuterung der Referenztypen und Werttypen geschrieben . Ich würde gerne auf alles eingehen, was Sie verwirrend finden.

Die "TL; DR" -Version soll den Wert einer Variablen / eines Ausdrucks eines bestimmten Typs berücksichtigen. Bei einem Werttyp ist der Wert die Information selbst. Bei einem Referenztyp ist der Wert eine Referenz, die null sein kann, oder eine Möglichkeit, zu einem Objekt zu navigieren, das die Informationen enthält.

Stellen Sie sich eine Variable beispielsweise wie ein Stück Papier vor. Es könnte den Wert "5" oder "false" haben, aber es könnte nicht mein Haus haben ... es müsste eine Wegbeschreibung zu meinem Haus haben. Diese Anweisungen entsprechen einer Referenz. Insbesondere könnten zwei Personen unterschiedliche Zettel haben, die die gleichen Anweisungen zu meinem Haus enthalten - und wenn eine Person diesen Anweisungen folgt und mein Haus rot streicht, würde die zweite Person diese Änderung ebenfalls sehen. Wenn beide nur separate Bilder von meinem Haus auf dem Papier hätten, würde eine Person, die ihr Papier färbt, das Papier der anderen Person überhaupt nicht ändern.


2
Es ist wichtig zu beachten, dass es drei verschiedene Haupttypen von Semantik gibt, die eine Sache bieten kann: unveränderliche Semantik, Semantik veränderbarer Werte und veränderbare Referenzsemantik. Konzeptionell ist die Art der Semantik, die ein Objekt implementiert, orthogonal dazu, ob es als eigenständiges Heap-Objekt oder als Variable / Feld (Struktur) gespeichert ist. Während Strukturen, die ihre Felder nicht verfügbar machen, in der Praxis jede Art von Semantik implementieren können, bedeutet die Tatsache, dass .net das promiskuitive Teilen von Heap-Referenzen ermöglicht, dass Heap-Objekte keine Semantik für veränderbare Werte implementieren können.
Supercat

Ich habe dieses bisschen nicht bekommen - 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. Was meinst du mit int, wobei bool bestimmte Typen sind? Alles in C #, z. B. int, bool, float, class, interface, delegate, ist ein Typ (Datentyp um genau zu sein). Datentypen werden in C # als 'Referenztyp' und 'Werttyp' getrennt. Warum sagen Sie dann, dass int ein bestimmter Typ ist, die Schnittstelle jedoch eine Art Typ?
RBT

2
@RBT: Datentypen werden nicht nur in "Referenztyp" und " Werttyp " getrennt. Sie sind auch in "Klasse, Struktur, Aufzählung, Delegat, Schnittstelle" unterteilt. intist eine Struktur, stringist eine Klasse, Actionist ein Delegat usw. Ihre Liste von "int, bool, float, Klasse, Schnittstelle, Delegat" ist eine Liste, die verschiedene Arten von Dingen enthält, genau wie "10, int" eine Liste mit verschiedenen Dingen.
Jon Skeet

@ JonSkeet Möglicherweise ist die Antwort auf diesen Beitrag dann etwas irreführend.
RBT

@RBT: Ich würde sagen, es ist etwas schlecht formuliert, aber nicht schrecklich.
Jon Skeet

26

Werttyp:

Enthält einen Wert, nicht Speicheradressen

Beispiel:

Struct

Lager:

TL; DR : Der Wert einer Variablen wird überall dort gespeichert, wo sie deklariert wird. Lokale Variablen befinden sich beispielsweise auf dem Stapel, aber wenn sie innerhalb einer Klasse als Mitglied deklariert werden, befinden sie sich auf dem Heap, der eng mit der Klasse gekoppelt ist, in der sie deklariert sind.
Länger : Somit werden Werttypen überall dort gespeichert, wo sie deklariert sind. Beispiel: intDer Wert eines in einer Funktion als lokale Variable wird auf dem Stapel gespeichert, während der intals Mitglied in einer Klasse deklarierte Wert von in auf dem Heap mit der Klasse gespeichert wird, in der er deklariert ist. Ein Werttyp auf Eine Klasse hat einen Lebenstyp, der genau dem entspricht, in dem sie deklariert ist, und erfordert fast keine Arbeit durch den Garbage Collector. Es ist jedoch komplizierter, ich würde mich auf @ JonSkeets Buch " C # In Depth " beziehen.Speicher in .NET "für eine präzisere Erklärung.

Vorteile:

Ein Werttyp benötigt keine zusätzliche Speicherbereinigung. Der Müll wird zusammen mit der Instanz gesammelt, in der er lebt. Lokale Variablen in Methoden werden beim Verlassen der Methode bereinigt.

Nachteile:

  1. Wenn ein großer Satz von Werten an eine Methode übergeben wird, kopiert die empfangende Variable tatsächlich, sodass zwei redundante Werte im Speicher vorhanden sind.

  2. Wenn der Unterricht verpasst wird, verliert er alle Vorteile

Referenztyp:

Enthält eine Speicheradresse mit einem Wert, der kein Wert ist

Beispiel:

Klasse

Lager:

Auf einem Haufen gespeichert

Vorteile:

  1. Wenn Sie eine Referenzvariable an eine Methode übergeben und diese ändert, ändert sich tatsächlich der ursprüngliche Wert, während bei Werttypen eine Kopie der angegebenen Variablen erstellt und dieser Wert geändert wird.

  2. Wenn die Größe der Variablen größer ist, ist der Referenztyp gut

  3. Da Klassen als Referenztypvariablen vorliegen, bieten sie Wiederverwendbarkeit und profitieren somit von der objektorientierten Programmierung

Nachteile:

Weitere Arbeitsreferenzen beim Zuweisen und Dereferenzen beim Lesen der value.extra-Überladung für den Garbage Collector


5
Es ist nicht unbedingt richtig, dass Referenztypen auf dem Heap und Werttypen auf dem Stapel gespeichert werden. Lesen Sie yoda.arachsys.com/csharp/memory.html, wenn Sie mehr erfahren möchten.
Rhys

1
Diese Antwort enthält viele Missverständnisse. Bitte lesen Sie Jeff Richters CLR über C #. Werttypen werden auf dem Thread-Stapel gespeichert und unterliegen keiner Garbage Collection (GC) - sie haben nichts mit GC zu tun. Referenztypen werden auf dem verwalteten Heap gespeichert und unterliegen daher der GC. Wenn ein Ref-Typ eine Root-Referenz hat, kann er nicht gesammelt werden und wird über die Generationen 0, 1 und 2 hochgestuft. Wenn er keine Root-Referenz hat, kann er Garbage Collected sein und durchläuft dann diesen Prozess namens Resurrection, wo er sich befindet wird getötet und wieder zum Leben erweckt und schließlich gesammelt.
Jeremy Thompson

13

Ich fand es einfacher, den Unterschied zwischen den beiden zu verstehen, wenn Sie wissen, wie Computer Dinge im Speicher zuordnen und wissen, was ein Zeiger ist.

Die Referenz ist normalerweise einem Zeiger zugeordnet. Dies bedeutet, dass die Speicheradresse, an der sich Ihre Variable befindet, tatsächlich eine andere Speicheradresse des tatsächlichen Objekts an einem anderen Speicherort enthält.

Das Beispiel, das ich geben werde, ist stark vereinfacht, nehmen Sie es also mit einem Körnchen Salz.

Stellen Sie sich vor, der Computerspeicher besteht aus mehreren Postfächern in einer Reihe (beginnend mit Postfach 0001 bis Postfach n), in denen sich etwas befinden kann. Wenn Postfächer dies nicht für Sie tun, versuchen Sie es mit einer Hashtabelle oder einem Wörterbuch oder einem Array oder ähnlichem.

Wenn Sie also etwas tun wie:

var a = "Hallo";

Der Computer führt folgende Schritte aus:

  1. Ordnen Sie Speicher zu (z. B. beginnend am Speicherort 1000 für 5 Bytes) und setzen Sie H (bei 1000), e (bei 1001), l (bei 1002), l (bei 1003) und o (bei 1004).
  2. irgendwo im Speicher zuordnen (z. B. an Position 0500) und als Variable a zuweisen.
    Es ist also wie ein Alias ​​(0500 ist a).
  3. Weisen Sie den Wert an diesem Speicherort (0500) 1000 zu (hier beginnt die Zeichenfolge Hello im Speicher). Somit enthält die Variable a einen Verweis auf den tatsächlichen Startspeicherort der "Hallo" -String.

Der Werttyp hält das eigentliche Objekt an seinem Speicherort.

Wenn Sie also etwas tun wie:

var a = 1;

Der Computer führt folgende Schritte aus:

  1. Weisen Sie einen Speicherort zu, z. B. um 05:00 Uhr, und weisen Sie ihn der Variablen a zu (dasselbe Alias-Ding).
  2. Geben Sie den Wert 1 ein (am Speicherplatz 0500).
    Beachten Sie, dass wir keinen zusätzlichen Speicher für den tatsächlichen Wert (1) reservieren. A enthält also tatsächlich den tatsächlichen Wert und wird daher als Werttyp bezeichnet.


@ Jon, Nun, diese Art macht das, was ich gesagt habe, ungültig, LOL. Aber wie gesagt, es ist stark vereinfacht, ein Verständnis zwischen den beiden Typen zu bekommen, was ich in meinem Fall hilfreich fand. Zumindest habe ich es mir so vorgestellt :).
Jimmy Chandra

8

Dies ist aus einem Beitrag von mir aus einem anderen Forum vor ungefähr zwei Jahren. Während die Sprache vb.net ist (im Gegensatz zu C #), sind die Konzepte für Werttyp und Referenztyp in .net einheitlich, und die Beispiele gelten weiterhin.

Es ist auch wichtig zu bedenken, dass in .net ALLE Typen technisch vom Basistyp Objekt abgeleitet sind. Die Werttypen sind so konzipiert, dass sie sich als solche verhalten, aber am Ende erben sie auch die Funktionalität des Basistyps Object.

A. Werttypen sind genau das - sie stellen einen bestimmten Bereich im Speicher dar, in dem ein diskreter WERT gespeichert ist. Werttypen haben eine feste Speichergröße und werden im Stapel gespeichert, der eine Sammlung von Adressen fester Größe ist.

Wenn Sie eine solche Aussage machen:

Dim A as Integer
DIm B as Integer

A = 3
B = A 

Sie haben Folgendes getan:

  1. Es wurden 2 Speicherplätze im Speicher erstellt, die ausreichen, um 32-Bit-Ganzzahlwerte aufzunehmen.
  2. Platzierte einen Wert von 3 in der Speicherzuordnung, die A zugewiesen wurde
  3. Platzieren Sie einen Wert von 3 in der Speicherzuweisung, die B zugewiesen wurde, indem Sie ihm denselben Wert wie in A zuweisen.

Der Wert jeder Variablen existiert diskret in jedem Speicherort.

B. Referenztypen können verschiedene Größen haben. Daher können sie nicht im "Stapel" gespeichert werden (denken Sie daran, dass der Stapel eine Sammlung von Speicherzuordnungen fester Größe ist?). Sie werden im "Managed Heap" gespeichert. Zeiger (oder "Verweise") auf jedes Element auf dem verwalteten Heap werden im Stapel verwaltet (wie eine Adresse). Ihr Code verwendet diese Zeiger im Stapel, um auf Objekte zuzugreifen, die im verwalteten Heap gespeichert sind. Wenn Ihr Code eine Referenzvariable verwendet, verwendet er tatsächlich einen Zeiger (oder eine "Adresse" auf einen Speicherort im verwalteten Heap).

Angenommen, Sie haben eine Klasse mit dem Namen clsPerson mit einer Zeichenfolge Property Person.Name erstellt

In diesem Fall, wenn Sie eine Aussage wie diese machen:

Dim p1 As clsPerson
p1 = New clsPerson
p1.Name = "Jim Morrison"

Dim p2 As Person

p2 = p1

Im obigen Fall gibt die Eigenschaft p1.Name erwartungsgemäß "Jim Morrison" zurück. Die Eigenschaft p2.Name gibt AUCH "Jim Morrison" zurück, wie Sie es intuitiv erwarten würden. Ich glaube, dass sowohl p1 als auch p2 unterschiedliche Adressen auf dem Stapel darstellen. Nachdem Sie p2 den Wert von p1 zugewiesen haben, zeigen sowohl p1 als auch p2 auf den GLEICHEN STANDORT auf dem verwalteten Heap.

Betrachten Sie jetzt diese Situation:

Dim p1 As clsPerson
Dim p2 As clsPerson

p1 = New clsPerson
p1.Name = "Jim Morrison"

p2 = p1

p2.Name = "Janis Joplin"

In dieser Situation haben Sie eine neue Instanz der Personenklasse auf dem verwalteten Heap mit einem Zeiger p1 auf dem Stapel erstellt, der auf das Objekt verweist, und der Name-Eigenschaft der Objektinstanz erneut den Wert "Jim Morrison" zugewiesen. Als Nächstes haben Sie einen weiteren Zeiger p2 im Stapel erstellt und ihn auf dieselbe Adresse auf dem verwalteten Heap gerichtet, auf die von p1 verwiesen wird (als Sie die Zuweisung p2 = p1 vorgenommen haben).

Hier kommt die Wendung. Wenn Sie der Eigenschaft Zuweisen des Namens von p2 den Wert "Janis Joplin" zuweisen, ändern Sie die Eigenschaft Name für das Objekt, auf das sowohl p1 als auch p2 verweisen, so dass Sie den folgenden Code ausführen:

MsgBox(P1.Name)
'Will return "Janis Joplin"

MsgBox(p2.Name)
'will ALSO return "Janis Joplin"Because both variables (Pointers on the Stack) reference the SAME OBJECT in memory (an Address on the Managed Heap). 

Hat das Sinn gemacht?

Zuletzt. Wenn du das tust:

DIm p1 As New clsPerson
Dim p2 As New clsPerson

p1.Name = "Jim Morrison"
p2.Name = "Janis Joplin"

Sie haben jetzt zwei unterschiedliche Personenobjekte. Sobald Sie dies jedoch erneut tun:

p2 = p1

Sie haben jetzt beide zurück auf "Jim Morrison" gezeigt. (Ich bin mir nicht ganz sicher, was mit dem Objekt auf dem Haufen passiert ist, auf das auf Seite 2 verwiesen wird. Ich denke, es ist jetzt außer Reichweite geraten. Dies ist einer der Bereiche, in denen mich hoffentlich jemand korrigieren kann.) -EDIT: Ich glaube, aus diesem Grund würden Sie p2 = Nothing ODER p2 = New clsPerson setzen, bevor Sie die neue Zuweisung vornehmen.

Noch einmal, wenn Sie jetzt DIESES tun:

p2.Name = "Jimi Hendrix"

MsgBox(p1.Name)
MsgBox(p2.Name)

Beide msgBoxen geben nun "Jimi Hendrix" zurück.

Dies kann für eine Weile ziemlich verwirrend sein, und ich werde ein letztes Mal sagen, dass ich möglicherweise einige Details falsch habe.

Viel Glück und hoffentlich kommen andere, die es besser wissen als ich, mit, um einiges davon zu klären. . .


Ich weiß nicht, warum Sie keine Up-Votes erhalten haben. Gute Antwort, half mir mit klaren, einfachen Beispielen zu verstehen.
Harry

Die Konzepte für Werttyp und Referenztyp sind in .net einheitlich und werden tatsächlich in der CLI-Spezifikation (Common Language Infrastructure), Ecma-Standard 335 (ebenfalls ein ISO-Standard) definiert. Dies ist der Standard für den Standardteil von .Net. Der Ecma-Standard 334 (auch ein ISO-Standard) ist die C # -Sprache und besagt ausdrücklich, dass C # -Implementierungen entweder auf der CLI basieren oder eine alternative Methode unterstützen müssen, um die für diesen C # -Standard erforderlichen Mindest-CLI-Funktionen zu erreichen . VB.Net ist jedoch kein Standard, sondern Eigentum von Microsoft.
user34660

5

Wertdatentyp und Referenzdatentyp

1) Wert (enthält die Daten direkt), aber Referenz (bezieht sich auf die Daten)

2) im Wert (jede Variable hat ihre eigene Kopie), aber
als Referenz (mehr als eine Variable kann sich auf einige Objekte beziehen)

3) im Wert (Operationsvariable kann sich nicht auf andere Variablen auswirken), aber in der Referenz (Variable kann andere beeinflussen)

4) Werttypen sind (int, bool, float), aber Referenztypen sind (Array, Klassenobjekte, Zeichenfolge)


2

Werttyp:

  • Feste Speichergröße.

  • Im Stapelspeicher gespeichert.

  • Hält den tatsächlichen Wert.

    Ex. int, char, bool, etc ...

Referenztyp:

  • Nicht fester Speicher.

  • Im Heap-Speicher gespeichert.

  • Enthält die Speicheradresse des tatsächlichen Werts.

    Ex. Zeichenfolge, Array, Klasse usw.


1

"Variablen, die auf Werttypen basieren, enthalten direkt Werte. Durch Zuweisen einer Werttypvariablen zu einer anderen wird der enthaltene Wert kopiert. Dies unterscheidet sich von der Zuweisung von Referenztypvariablen, die eine Referenz auf das Objekt, jedoch nicht auf das Objekt selbst kopiert." aus der Microsoft-Bibliothek.

Eine vollständigere Antwort finden Sie hier und hier .


1
Diese Erklärung gefällt mir nicht, weil es so klingt, als würde die Zuweisung für Referenztypen und Werttypen unterschiedlich funktionieren. Das tut es nicht. In beiden Fällen entspricht der Wert der Variablen "Ziel" dem Ausdruck - der Wert wird kopiert. Der Unterschied besteht darin, was dieser Wert ist - für Referenztypen ist der Wert, der kopiert wird, eine Referenz. Das ist aber immer noch der Wert der Variablen.
Jon Skeet

Ich stimme Ihnen zu und habe bereits gewusst, dass es anders sein kann, wie Sie in diesem Artikel lesen können . Aber ich überarbeite nur den Microsoft-Leitfaden zu diesem Thema und auch dazu, wie Sie normalerweise in Büchern lesen. Bitte beschuldigen Sie mich nicht! :)
Lucas S.

Oh sicher ... es gibt viele Teile der MSDN-Dokumentation, in denen Fehler zu finden sind :)
Jon Skeet

1

Manchmal helfen Erklärungen nicht besonders für Anfänger. Sie können sich den Werttyp als Datendatei und den Referenztyp als Verknüpfung zu einer Datei vorstellen.

Wenn Sie also eine Referenzvariable kopieren, kopieren Sie nur den Link / Zeiger auf reale Daten irgendwo im Speicher. Wenn Sie einen Werttyp kopieren, klonen Sie die Daten wirklich im Speicher.


0

Dies ist wahrscheinlich in esoterischer Hinsicht falsch, aber um es einfach zu machen:

Werttypen sind Werte, die normalerweise "nach Wert" übergeben werden (also kopieren). Referenztypen werden "als Referenz" übergeben (wodurch ein Zeiger auf den ursprünglichen Wert gegeben wird). Der .NET ECMA-Standard gibt keine Garantie dafür, wo diese "Dinge" gespeichert werden. Sie könnten eine Implementierung von .NET erstellen, die stapellos oder schwerelos ist (die zweite wäre sehr komplex, aber Sie könnten wahrscheinlich Fasern und viele Stapel verwenden).

Strukturen sind Werttypen (int, bool ... sind Strukturen oder werden zumindest als ... simuliert), Klassen sind Referenztypen.

Werttypen stammen von System.ValueType ab. Der Referenztyp stammt von System.Object ab.

Jetzt .. Am Ende haben Sie Werttyp, "referenzierte Objekte" und Referenzen (in C ++ würden sie Zeiger auf Objekte genannt. In .NET sind sie undurchsichtig. Wir wissen nicht, was sie sind. Aus unserer Sicht sind sie sind "Handles" für das Objekt). Diese Leisten ähneln Werttypen (sie werden per Kopie übergeben). Ein Objekt besteht also aus dem Objekt (einem Referenztyp) und null oder mehr Verweisen darauf (die Werttypen ähnlich sind). Wenn es keine Referenzen gibt, wird der GC diese wahrscheinlich sammeln.

Im Allgemeinen (in der "Standard" -Implementierung von .NET) kann der Werttyp auf den Stapel (wenn es sich um lokale Felder handelt) oder auf den Heap (wenn es sich um Felder einer Klasse handelt, wenn es sich um Variablen in einer Iteratorfunktion handelt) handeln. wenn es sich um Variablen handelt, auf die durch einen Abschluss verwiesen wird, wenn sie in einer asynchronen Funktion variabel sind (unter Verwendung des neueren asynchronen CTP) ...). Der referenzierte Wert kann nur zum Heap gehen. Referenzen verwenden dieselben Regeln wie Werttypen.

In den Fällen von Werttyp, die auf dem Heap gespeichert werden, weil sie sich in einer Iteratorfunktion oder einer asynchronen Funktion befinden oder durch einen Abschluss referenziert werden, wird beim Betrachten der kompilierten Datei festgestellt, dass der Compiler eine Klasse zum Einfügen dieser Variablen erstellt hat und die Klasse wird erstellt, wenn Sie die Funktion aufrufen.

Jetzt weiß ich nicht, wie man lange Dinge schreibt, und ich habe bessere Dinge in meinem Leben zu tun. Wenn Sie eine "präzise" "akademische" "richtige" Version wünschen, lesen Sie DIESES:

http://blogs.msdn.com/b/ericlippert/archive/2010/09/30/the-truth-about-value-types.aspx

Es ist 15 Minuten, ich suche es! Es ist besser als die msdn-Versionen, da es sich um einen komprimierten "gebrauchsfertigen" Artikel handelt.


1
Es ist in mehr als esoterischer Hinsicht falsch. Es ist grundsätzlich falsch, würde ich sagen - weil Referenztypwerte immer noch auch als Wert übergeben werden; Es ist nur so, dass der Wert eine Referenz ist, kein Objekt. Siehe pobox.com/~skeet/csharp/parameters.html . Oh, und lokale Variablen können auch auf dem Heap landen, beispielsweise wenn sie erfasst werden oder Teil eines Iteratorblocks sind.
Jon Skeet

Iteratorblöcke werden in Klassen konvertiert, also "hinter Ihnen" sind sie "Felder einer Klasse". Gleiches gilt für Verschlüsse. Ja ... ich habe vergessen, die Unterscheidung zwischen dem "Zeiger" (der Referenz) und dem "spitzen" zu schreiben
xanatos

@xanatos: Sicher, sie sind nach der Kompilierung Felder einer Klasse - aber sie sind immer noch lokale Variablen im Quellcode. Ich würde die Referenzen selbst auch nicht als "Werttypen" bezeichnen - ich glaube, ich weiß, woher Sie kommen, aber ich denke nicht, dass es eine gute Idee ist, das Wasser auf diese Weise zu trüben.
Jon Skeet

@jon Ja ... Sie sind ein dritter Typ, da Zeiger in .net "undurchsichtig" sind und nicht von ValueType abgeleitet sind. Sie ähneln jedoch eher Werttypen als Referenzen. Sie können sie "ref" und "out". Ich musste das Wasser schlammig machen, weil "jemand" die Arbeit von Iteratoren nicht auswählen musste.
Xanatos

Beim Betrachten des Artikels, auf den ich jetzt verweise, habe ich festgestellt: "Es gibt drei Arten von Werten: (1) Instanzen von Werttypen, (2) Instanzen von Referenztypen und (3) Referenzen. (Code in C # kann nicht manipuliert werden Instanzen von Referenztypen direkt; dies geschieht immer über eine Referenz. In unsicherem Code werden Zeigertypen wie Werttypen behandelt, um die Speicheranforderungen ihrer Werte zu bestimmen. ) ".
Xanatos

0

Der einfachste Weg, sich Referenztypen vorzustellen, besteht darin, sie als "Objekt-IDs" zu betrachten. Die einzigen Dinge, die man mit einer Objekt-ID tun kann, sind, eine zu erstellen, eine zu kopieren, den Typ einer zu erfragen oder zu manipulieren oder zwei auf Gleichheit zu vergleichen. Der Versuch, mit einer Objekt-ID etwas anderes zu tun, wird als Abkürzung für die angegebene Aktion mit dem Objekt angesehen, auf das sich diese ID bezieht.

Angenommen, ich habe zwei Variablen X und Y vom Typ Auto - einen Referenztyp. Y enthält zufällig "Objekt-ID # 19531". Wenn ich "X = Y" sage, hält X "Objekt-ID # 19531". Beachten Sie, dass weder X noch Y ein Auto halten. Das Auto, auch bekannt als "Objekt-ID # 19531", wird an anderer Stelle gelagert. Als ich Y in X kopierte, kopierte ich nur die ID-Nummer. Angenommen, ich sage X.Color = Colors.Blue. Eine solche Anweisung wird als Anweisung angesehen, "Objekt-ID # 19531" zu suchen und blau zu malen. Beachten Sie, dass X und Y zwar jetzt eher auf ein blaues als auf ein gelbes Auto verweisen, die Anweisung jedoch weder X noch Y betrifft, da sich beide immer noch auf "Objekt-ID # 19531" beziehen, bei dem es sich immer noch um dasselbe Auto handelt war schon immer.


0

Variablentypen und Referenzwerte sind einfach anzuwenden und gut auf das Domänenmodell anzuwenden, was den Entwicklungsprozess erleichtert.

Um jeden Mythos über die Menge des "Werttyps" zu entfernen, werde ich kommentieren, wie dies auf der Plattform gehandhabt wird. NET, speziell in C # (CSharp), wenn APIS aufgerufen wird, und Parameter nach Wert, Referenz, in unseren Methoden und Funktionen senden und wie die Passagen dieser Werte korrekt behandelt werden.

Lesen Sie diesen Artikel Wert und Referenz des Variablentyps in C #


Dies ist leider nur eine englischsprachige Q & A-Site = \. Vielen Dank, dass Sie versucht haben zu antworten. Bitte erstellen Sie vollständige Antworten mit Links nur als Hilfsmittel (jedoch nicht als vollständige nachhaltige Antwort). Bitte schauen Sie sich an, wie Sie antworten können .
Jesse

0

Angenommen, es vhandelt sich um einen Ausdruck / eine Variable vom Werttyp und reinen Ausdruck / eine Variable vom Referenztyp

    x = v  
    update(v)  //x will not change value. x stores the old value of v

    x = r 
    update(r)  //x now refers to the updated r. x only stored a link to r, 
               //and r can change but the link to it doesn't .

Eine Variable vom Typ Wert speichert also den tatsächlichen Wert (5 oder "h"). Eine Variable vom Referenztyp speichert nur einen Link zu einem metaphorischen Feld, in dem sich der Wert befindet.


0

Bevor Sie die verschiedenen in C # verfügbaren Datentypen erläutern, müssen Sie erwähnen, dass C # eine stark typisierte Sprache ist. Dies bedeutet, dass jede Variable, Konstante, Eingabeparameter, Rückgabetyp und im Allgemeinen jeder Ausdruck, der einen Wert ergibt, einen Typ hat.

Jeder Typ enthält Informationen, die vom Compiler als Metadaten in die ausführbare Datei eingebettet werden und von der Common Language Runtime (CLR) verwendet werden, um die Typensicherheit beim Zuweisen und Zurückfordern von Speicher zu gewährleisten.

Wenn Sie wissen möchten, wie viel Speicher ein bestimmter Typ zuweist, können Sie den Operator sizeof wie folgt verwenden:

static void Main()
{
    var size = sizeof(int);
    Console.WriteLine($"int size:{size}");
    size = sizeof(bool);
    Console.WriteLine($"bool size:{size}");
    size = sizeof(double);
    Console.WriteLine($"double size:{size}");
    size = sizeof(char);
    Console.WriteLine($"char size:{size}");
}

Die Ausgabe zeigt die Anzahl der von jeder Variablen zugewiesenen Bytes.

int size:4
bool size:1
double size:8
char size:2

Die Informationen zu jedem Typ sind:

  • Der benötigte Speicherplatz.
  • Die Maximal- und Minimalwerte. Beispielsweise akzeptiert der Typ Int32 Werte zwischen 2147483648 und 2147483647.
  • Der Basistyp, von dem es erbt.
  • Der Speicherort, an dem der Speicher für Variablen zur Laufzeit zugewiesen wird.
  • Die Arten von Operationen, die zulässig sind.
  • Die im Typ enthaltenen Elemente (Methoden, Felder, Ereignisse usw.). Wenn wir beispielsweise die Definition des Typs int überprüfen, finden wir die folgende Struktur und die folgenden Elemente:

    namespace System
    {
        [ComVisible(true)]
        public struct Int32 : IComparable, IFormattable, IConvertible, IComparable<Int32>, IEquatable<Int32>
        {      
            public const Int32 MaxValue = 2147483647;     
            public const Int32 MinValue = -2147483648;
            public static Int32 Parse(string s, NumberStyles style, IFormatProvider provider);    
            ... 
        }  
    }

Speicherverwaltung Wenn auf einem Betriebssystem mehrere Prozesse ausgeführt werden und die RAM-Größe nicht ausreicht, um alles zu speichern, ordnet das Betriebssystem Teile der Festplatte dem RAM zu und beginnt mit dem Speichern von Daten auf der Festplatte. Das Betriebssystem verwendet als bestimmte Tabellen, in denen virtuelle Adressen ihren entsprechenden physischen Adressen zugeordnet sind, um die Anforderung auszuführen. Diese Funktion zum Verwalten des Speichers wird als virtueller Speicher bezeichnet.

In jedem Prozess ist der verfügbare virtuelle Speicher in den folgenden 6 Abschnitten organisiert. Für die Relevanz dieses Themas konzentrieren wir uns jedoch nur auf den Stapel und den Heap.

Stapel Der Stapel ist eine LIFO-Datenstruktur (last in, first out) mit einer vom Betriebssystem abhängigen Größe (standardmäßig für ARM-, x86- und x64-Computer). Windows reserviert 1 MB, während Linux je nach Betriebssystem zwischen 2 MB und 8 MB reserviert Ausführung).

Dieser Speicherbereich wird automatisch von der CPU verwaltet. Jedes Mal, wenn eine Funktion eine neue Variable deklariert, weist der Compiler einen neuen Speicherblock zu, der so groß ist wie seine Größe auf dem Stapel, und wenn die Funktion beendet ist, wird der Speicherblock für die Variable freigegeben.

Heap Dieser Speicherbereich wird von der CPU nicht automatisch verwaltet und ist größer als der Stapel. Wenn das neue Schlüsselwort aufgerufen wird, sucht der Compiler nach dem ersten freien Speicherblock, der der Größe der Anforderung entspricht. und wenn es es findet, wird es mit der eingebauten C-Funktion malloc () als reserviert markiert und gibt den Zeiger auf diese Position zurück. Es ist auch möglich, die Zuweisung eines Speicherblocks mithilfe der integrierten C-Funktion free () aufzuheben. Dieser Mechanismus verursacht eine Speicherfragmentierung und muss Zeiger verwenden, um auf den richtigen Speicherblock zuzugreifen. Er ist langsamer als der Stapel, um die Lese- / Schreiboperationen auszuführen.

Benutzerdefinierte und integrierte Typen Während C # einen Standardsatz integrierter Typen bereitstellt, die Ganzzahlen, Boolesche Werte, Textzeichen usw. darstellen, können Sie Konstrukte wie struct, class, interface und enum verwenden, um Ihre eigenen Typen zu erstellen.

Ein Beispiel für einen benutzerdefinierten Typ, der das Strukturkonstrukt verwendet, ist:

struct Point
{
    public int X;
    public int Y;
};

Wert- und Referenztypen Wir können den C # -Typ in die folgenden Kategorien einteilen:

  • Werttypen
  • Referenztypen

Werttypen Werttypen ergeben sich aus der System.ValueType Klasse und Variablen dieses Typs enthalten ihre Werte innerhalb ihrer Speicherzuweisung in dem Stapel. Die beiden Kategorien von Werttypen sind struct und enum.

Das folgende Beispiel zeigt das Mitglied vom Typ Boolean. Wie Sie sehen, gibt es keinen expliziten Verweis auf die System.ValueType-Klasse. Dies geschieht, weil diese Klasse von der Struktur geerbt wird.

namespace System
{
    [ComVisible(true)]
    public struct Boolean : IComparable, IConvertible, IComparable<Boolean>, IEquatable<Boolean>
    {
        public static readonly string TrueString;
        public static readonly string FalseString;
        public static Boolean Parse(string value);
        ...
    }
}

Referenztypen Andererseits enthalten die Referenztypen nicht die tatsächlichen Daten, die in einer Variablen gespeichert sind, sondern die Speicheradresse des Heaps, in dem der Wert gespeichert ist. Die Kategorien von Referenztypen sind Klassen, Delegaten, Arrays und Schnittstellen.

Wenn zur Laufzeit eine Referenztypvariable deklariert wird, enthält sie den Wert null, bis ihr ein Objekt zugewiesen wird, das mit den Schlüsselwörtern new erstellt wurde.

Das folgende Beispiel zeigt die Mitglieder des generischen Typs List.

namespace System.Collections.Generic
{
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(Generic.Mscorlib_CollectionDebugView<>))]
    [DefaultMember("Item")]
    public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        ...
        public T this[int index] { get; set; }
        public int Count { get; }
        public int Capacity { get; set; }
        public void Add(T item);
        public void AddRange(IEnumerable<T> collection);
        ...
    }
}

Wenn Sie die Speicheradresse eines bestimmten Objekts ermitteln möchten, bietet die Klasse System.Runtime.InteropServices eine Möglichkeit, aus dem nicht verwalteten Speicher auf verwaltete Objekte zuzugreifen. Im folgenden Beispiel verwenden wir die statische Methode GCHandle.Alloc (), um einem String ein Handle zuzuweisen, und dann die Methode AddrOfPinnedObject, um seine Adresse abzurufen.

string s1 = "Hello World";
GCHandle gch = GCHandle.Alloc(s1, GCHandleType.Pinned);
IntPtr pObj = gch.AddrOfPinnedObject();
Console.WriteLine($"Memory address:{pObj.ToString()}");

Die Ausgabe wird sein

Memory address:39723832

Referenzen Offizielle Dokumentation: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019


-1

Es gibt viele kleine Details zu den Unterschieden zwischen Werttypen und Referenztypen, die vom Standard explizit angegeben werden, und einige davon sind insbesondere für Anfänger nicht leicht zu verstehen.

Siehe ECMA- Standard 33, Common Language Infrastructure (CLI) . Die CLI ist auch durch die ISO standardisiert. Ich würde eine Referenz bereitstellen, aber für ECMA müssen wir ein PDF herunterladen und dieser Link hängt von der Versionsnummer ab. ISO-Standards kosten Geld.

Ein Unterschied besteht darin, dass Werttypen eingerahmt werden können, Referenztypen jedoch im Allgemeinen nicht. Es gibt Ausnahmen, aber sie sind ziemlich technisch.

Werttypen dürfen keine parameterlosen Instanzkonstruktoren oder Finalizer haben und können nicht auf sich selbst verweisen. Sich auf sich selbst zu beziehen bedeutet zum Beispiel, dass ein Mitglied von Node kein Node sein kann , wenn es einen Werttyp Node gibt . Ich denke, es gibt andere Anforderungen / Einschränkungen in den Spezifikationen, aber wenn ja, dann werden sie nicht an einem Ort zusammengefasst.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.