Alle eigenständigen Heap-Objekte erben von Object; Dies ist sinnvoll, da alle eigenständigen Heap-Objekte bestimmte gemeinsame Aspekte aufweisen müssen, z. B. die Identifizierung ihres Typs. Wenn der Garbage-Collector einen Verweis auf ein Heap-Objekt eines unbekannten Typs hätte, hätte er keine Möglichkeit zu wissen, welche Bits in dem diesem Objekt zugeordneten Speicherblock als Verweise auf andere Heap-Objekte anzusehen sind.
Ferner ist es innerhalb des Typsystems zweckmäßig, den gleichen Mechanismus zum Definieren der Mitglieder von Strukturen und der Mitglieder von Klassen zu verwenden. Das Verhalten von Speicherorten vom Typ "Wert" (Variablen, Parameter, Felder, Array-Slots usw.) unterscheidet sich stark von dem von Speicherorten vom Typ "Klasse". Solche Verhaltensunterschiede treten jedoch bei den Quellcode-Compilern und der Ausführungs-Engine (einschließlich) auf JIT-Compiler), anstatt im Typsystem ausgedrückt zu werden.
Eine Konsequenz davon ist, dass das Definieren eines Werttyps effektiv zwei Typen definiert - einen Speicherorttyp und einen Heap-Objekttyp. Ersteres kann implizit in Letzteres konvertiert werden, und Letzteres kann durch Typumwandlung in Ersteres konvertiert werden. Beide Arten der Konvertierung funktionieren, indem alle öffentlichen und privaten Felder von einer Instanz des betreffenden Typs in eine andere kopiert werden. Darüber hinaus ist es mit generischen Einschränkungen möglich, Schnittstellenelemente an einem Speicherort vom Werttyp direkt aufzurufen, ohne zuvor eine Kopie davon zu erstellen.
All dies ist wichtig, da Verweise auf Heap-Objekte vom Typ Wert sich wie Klassenverweise und nicht wie Werttypen verhalten. Betrachten Sie beispielsweise den folgenden Code:
string testEnumerator <T> (T it) wobei T: IEnumerator <string>
{
var it2 = it;
it.MoveNext ();
it2.MoveNext ();
return it.Current;
}
öffentlicher ungültigkeitstest ()
{
var theList = new List <string> ();
theList.Add ("Fred");
theList.Add ("George");
theList.Add ("Percy");
theList.Add ("Molly");
theList.Add ("Ron");
var enum1 = theList.GetEnumerator ();
IEnumerator <string> enum2 = enum1;
Debug.Print (testEnumerator (enum1));
Debug.Print (testEnumerator (enum1));
Debug.Print (testEnumerator (enum2));
Debug.Print (testEnumerator (enum2));
}
Wenn der testEnumerator()Methode ein Speicherort vom Werttyp übergeben wird, itwird eine Instanz empfangen, deren öffentliche und private Felder aus dem übergebenen Wert kopiert werden. Die lokale Variable enthält it2eine andere Instanz, von der alle Felder kopiert wurden it. Der Aufruf MoveNextauf it2keinen Einfluss auf it.
Wenn der obige Code an einen Speicherort des Klassentyps übergeben wird, beziehen sich die übergebenen Werte itund it2alle auf dasselbe Objekt, und wenn MoveNext()einer von ihnen aufgerufen wird, wird dies effektiv für alle von ihnen aufgerufen.
Beachten Sie, dass Casting, List<String>.Enumeratorum es IEnumerator<String>effektiv von einem Werttyp in einen Klassentyp umzuwandeln. Der Typ des Heap-Objekts ist, List<String>.Enumeratoraber sein Verhalten unterscheidet sich stark vom gleichnamigen Werttyp.
objectim .NET-Framework, zum Teil, weil es einige grundlegende Funktionen für alle Objekte bereitstellt, wie z. B.ToString()undGetHashCode()