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
, interface
usw. Oder ist dies falsch, auch?
Können Sie es mir professionell erklären?
c#
.net
value-type
reference-type
Tugberk
quelle
quelle
Antworten:
Ihre Beispiele sind ein wenig seltsam , weil während
int
,bool
undfloat
sind bestimmte Typen, Schnittstellen und Delegierten sind Arten von Typ - wiestruct
undenum
sind 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.
quelle
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?int
ist eine Struktur,string
ist eine Klasse,Action
ist 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.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:
int
Der Wert eines in einer Funktion als lokale Variable wird auf dem Stapel gespeichert, während derint
als 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:
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.
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:
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.
Wenn die Größe der Variablen größer ist, ist der Referenztyp gut
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
quelle
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:
Es ist also wie ein Alias (0500 ist a).
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:
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.
quelle
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:
Sie haben Folgendes getan:
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:
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:
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:
Hat das Sinn gemacht?
Zuletzt. Wenn du das tust:
Sie haben jetzt zwei unterschiedliche Personenobjekte. Sobald Sie dies jedoch erneut tun:
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:
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. . .
quelle
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)
quelle
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.
quelle
"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 .
quelle
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.
quelle
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.
quelle
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.
quelle
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 #
quelle
Angenommen, es
v
handelt sich um einen Ausdruck / eine Variable vom Werttyp undr
einen Ausdruck / eine Variable vom ReferenztypEine 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.
quelle
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:
Die Ausgabe zeigt die Anzahl der von jeder Variablen zugewiesenen Bytes.
Die Informationen zu jedem Typ 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:
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:
Wert- und Referenztypen Wir können den C # -Typ in die folgenden Kategorien einteilen:
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.
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.
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.
Die Ausgabe wird sein
Referenzen Offizielle Dokumentation: https://docs.microsoft.com/en-us/cpp/build/reference/stack-stack-allocations?view=vs-2019
quelle
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.
quelle