Nice Box - Was ist drin?

Veröffentlicht: 16. Dez 2001 | Aktualisiert: 16. Jun 2004

Von Eric Gunnerson

Letzten Monat haben wir gelernt, wie wir Dinge finden können, die mit C# in Beziehung stehen. Ich habe einige Anfragen erhalten, Websites zu C# und .NET zu nennen. Also habe ich mich entschieden, meiner Kolumne einen Abschnitt mit Websites hinzuzufügen. Senden Sie mir Ihre Site. Ich werde jeden Monat fünf beliebige Sites auswählen und die URLs am Ende des Artikels aufführen.
Genug Vorgeplänkel, es ist Zeit für etwas Spaß.

* * *

Auf dieser Seite

Typen in C# Typen in C#
Der Weg zu einem einfacheren Modell Der Weg zu einem einfacheren Modell
Verpacken und Entpacken Verpacken und Entpacken
Quiz: Kennen Sie sich mit Paketen aus? Quiz: Kennen Sie sich mit Paketen aus?
Antworten Antworten
Auswertung Auswertung
Zusammenfassung Zusammenfassung
C#-Websites C#-Websites

Typen in C#

In C# und der gemeinsamen Sprachlaufzeit (Common Language Runtime, CLR) gibt es zwei Arten von Typen: Referenztypen (in C# durch Klassen deklariert) und Werttypen (in C# durch Strukturen deklariert). Referenz- und Werttypen weisen einige wichtige Unterschiede auf. Diese Unterschiede sind in der folgenden Tabelle aufgeführt:

Referenz (Klasse)

Wert (Struktur)

Variable enthält

Referenz

Tatsächlichen Wert

Ort des Wertes

Im Heap

Inline (auf dem Stapel, oder inline mit einem Objekt)

Standardwert

Null

Auf Null gesetzt

= bedeutet

Referenz kopieren

Wert kopieren

Werttypen sind Typen, die sich wie ein Datenelement "verhalten". Dies beinhaltet vordefinierte numerische Typen und benutzerdefinierte Typen wie eine Komplexe Zahl, einen Punkt oder ein Rechteck. Wie oben schon gezeigt, enthält die Variable eines Werttyps den tatsächlichen Wert. Wenn Sie diese Variable also verwenden, arbeiten Sie immer mit dem tatsächlichen Wert.

int i = 123; 
int j = i; 
i = 55;

Nach der zweiten Zuweisung gibt es zwei unabhängige Variablen, die den gleichen Wert enthalten. Eine Änderung des Wertes "i" wirkt sich nicht auf den Wert "j" aus.
Referenztypen werden für alle Objekte verwendet, die als Werttypen keinen Sinn ergeben. Eine Variable eines Referenztyps verweist auf die Instanz des Objekts im Heap. Wenn Sie eine Variable also einer anderen Variablen zuweisen, weisen Sie nur die Referenz zu, nicht den Wert.

Employee e = new Employee("Fred"); 
Employee f = e; 
f.Name = "Barney";

Nach der zweiten Zuweisung zeigen sowohl "e" als auch "f" auf das gleiche Objekt. Dies bedeutet, dass bei einer Änderung des Namens von "f" auch der Name der Variablen "e" geändert wird, da beide auf die gleiche Instanz verweisen.
Dazu ist noch etwas zu sagen. Einige von Ihnen haben sich vielleicht gefragt, warum die Funktionen in der System.String-Klasse die Zeichenfolge nicht ändern, sondern immer eine neue Version der Zeichenfolge zurückgeben. Dies liegt daran, dass eine Zeichenfolge ein Referenztyp ist. Wenn das Aufrufen von s.Trim() für eine Zeichenfolge die interne Zeichenfolge ändern würde, würde der gleiche Fall wie oben bei "Employee" auftreten (was für Zeichenfolgen nicht sehr gut wäre).
Member, die den Wert einer Klasse ändern, werden als Mutatoren bezeichnet, und eine Klasse, die keine Mutatoren hat, wird als unveränderbare Klasse bezeichnet (Engl.: immutable). Mit Hilfe von unveränderbaren Klassen erhalten Sie eine Klasse, die sich wie eine Wertklasse verhält, aber nicht so geschrieben werden kann.
Wenn Sie eine veränderbare Zeichenfolgenklasse benötigen, können Sie die StringBuilder-Klasse aus System.Text ausprobieren.

 

Der Weg zu einem einfacheren Modell

Es ist wichtig, dass eine Sprache sowohl Referenztypen als auch Werttypen aufweist. Werttypen sind einfach und effizient, und Referenztypen werden für die objektorientierte Entwicklung benötigt. Eigentlich hätten wir anstelle von zwei Arten von Typen jedoch lieber ein simpleres Modell mit nur einem Typ, der jeden beliebigen Wert enthalten könnte.
Bei der Verwendung eines solchen universellen Grundtyps wäre Folgendes möglich:

  • Aufrufen einer virtuellen Funktion für beliebige Werte

  • Schreiben von Auflistungsklassen, die beliebige Werte speichern können

  • Ersetzen des Variant-Typs für die OLE-Automation

Bei der gemeinsamen Sprachlaufzeit ist es möglich, Werttypen als Referenztypen zu verwenden, um dieses Ziel zu erreichen. Dieser Prozess wird als "Boxing" bzw. Verpacken bezeichnet. Eine Variable vom Typ "Objekt", also der universelle Grundtyp, kann dann auf einen mit Hilfe des Boxing verpackten Typ verweisen.

 

Verpacken und Entpacken

Sehen Sie sich den folgenden Code an:

int value = 123; 
object o = value;   // int in Objektpaket verpacken 
int value2 = (int) o;   // in value2 entpacken 

Wenn "value" dem Objekt "o" zugewiesen wird, erstellt der C#-Compiler als Teil der Zuweisung ein Referenztyppaket, das groß genug ist, um int im Heap zu enthalten. Danach kopiert er den Wert in dieses Paket und versieht das Paket mit dem tatsächlichen Typ (hier System.Int32), damit die Laufzeit weiß, welcher Typ sich im Paket befindet.

Dabei muss durch einen Cast angegeben werden, welcher Typ sich vermutlich im Paket befindet (da Objekte beliebige Typen enthalten können), um einen Wert aus einem Paket entnehmen zu können.Während der Ausführung prüft die Laufzeit, ob es sich bei dem Typ, auf den die Objektvariable verweist, um den im Cast angegebenen Typ handelt. Wenn es der richtige Typ ist, wird der Wert aus dem Paket zurück in die Werttypvariable kopiert. Wenn nicht, wird eine Ausnahme ausgelöst.

Beachten Sie dabei, dass während des Entpackens keine weiteren Konvertierungen durchgeführt werden können. Die Typen müssen exakt übereinstimmen. Wenn wir also Folgendes schreiben:

long value2 = (long) o;   // Verpackter Wert (value) ist int

und es sich bei "o" um eine verpackte Ganzzahl, also int, handelt, wird eine Ausnahme ausgelöst. Wir können jedoch auch Folgendes schreiben:

long value2 = (long)(int) o;

Die Konvertierung funktioniert in diesem Fall wie erwartet.
Dieses Beispiel zeigt zwar das Ver- und Entpacken, ist jedoch etwas irreführend. Es ist eher selten, dass Code geschrieben wird, der dann das Verpacken übernimmt. Normalerweise geschieht dies, wenn Sie eine Werttypvariable an einen Parameter eines Typobjekts übergeben.
Jetzt ist es Zeit für ein Quiz. Ich hoffe, Sie haben alle aufgepasst.

 

Quiz: Kennen Sie sich mit Paketen aus?

Im folgenden Codeabschnitt sehen Sie verschiedene Szenarios. Entscheiden Sie jeweils, wo verpackt wird und wo nicht. Bei einigen Szenarios sollten Sie an mehr als einer Stelle nachsehen (z.B. bei B).

// Szenario 1 
int total = 35; 
DateTime date = DateTime.Now; 
string s = String.Format("Gesamtsumme lautete {0} am {1}", total, date); 
// Szenario B 
Hashtable t = new Hashtable(); 
t.Add(0, "zero"); 
t.Add(1, "one"); 
// Szenario c 
DateTime d = DateTime.Now; 
String s = d.ToString(); 
// Szenario IV 
int[] a = new int[2]; 
a[0] = 33; 
// Szenario 101 
ArrayList a = new ArrayList(); 
a.Add(33); 
// Szenario vi 
MyStruct s = new MyStruct(15); 
IProcess ip = (IProcess) s; 
ip.Process(); 

 

Antworten

Lesen Sie sich die Antworten durch, und zählen Sie Ihre richtigen Antworten.

Szenario 1

String.Format() benötigt als ersten Parameter eine Zeichenfolge und Objekte als zweiten und dritten Parameter. Sowohl bei int als auch bei DateTime handelt es sich um Werttypen, also werden sie für den zweiten und dritten Parameter verpackt. String.Format() übernimmt diese Parameter und ruft für jeden object.ToString() auf, um eine Konvertierung in eine Zeichenfolgendarstellung durchzuführen. Einen Punkt erhalten Sie für die Antwort, dass int verpackt wird, und einen Punkt für DateTime.

Szenario B

Hashtable.Add() benötigt zwei Parameter, einen für den Schlüssel und einen für den Wert. Beide haben den Typ "Objekt". Der für den Schlüssel übergebene Wert ist eine Ganzzahl, also muss er verpackt werden, um als Objekt übergeben werden zu können. Der für den Wert übergebene Wert ist eine Zeichenfolge, die ein Referenztyp ist, also wird die Zeichenfolge nicht verpackt. Jeweils ein Punkt für die richtige Antwort.

Szenario c

Bei diesem Szenario ist ein Trick eingebaut. Eines der Ziele beim Verpacken besteht darin, virtuelle Funktionen für Werttypen aufrufen zu können. ToString() ist eine virtuelle Funktion für "object", also sieht es so aus, als ob "d" verpackt wird, wenn ToString() aufgerufen wird. Wir haben "d" jedoch nicht so verwendet, dass dieses Element in ein Objekt konvertiert wird, also ist keine Verpackung erforderlich. Der Compiler weiß, dass eine Variable vom Typ DateTime nur diesen Typ haben kann (es kann sich nicht um einen abgeleiteten Typ handeln, da es keine abgeleiteten Werttypen gibt). Also kann der Compiler DateTime.ToString() direkt aufrufen und die Referenz "this" so festlegen, dass sie auf die Instanz "d" auf dem Stapel zeigt. Einen Punkt für die richtige Antwort.

Szenario IV

Arrays speichern ihre Werte in der gemeinsamen Sprachlaufzeit direkt. Ein int-Array mit fünf Elementen ordnet z.B. genügend Platz zum Speichern von fünf "ints" zu (nicht für fünf Objekte). Ein Punkt, wenn Sie erkannt haben, dass hier keine Verpackung stattfindet.

Szenario 101

ArrayList.Add() benötigt ein Objekt als Parameter, also wird die Ganzzahl 33 verpackt. Ein Punkt für die richtige Antwort.

Szenario vi

Schnittstellen sind Referenztypen. Wenn Sie einen Werttyp also an eine Schnittstelle casten, die diesen Typ implementiert, muss der Werttyp verpackt sein. Ein Punkt für die richtige Antwort.

 

Auswertung

Zählen Sie Ihre Punkte zusammen, und sehen Sie in der folgenden Tabelle nach, wie gut Sie sich mit dem "Boxing" auskennen:

Ergebnis

Beschreibung

8

Der Inbegriff des Boxingexperten

6-7

Sehr gute Kenntnisse, aber noch inkonsistent

3-5

Angehender Experte

1-2

Suchen Sie sich einen kompetenten Lehrer

0

Bleiben Sie bei Ihrer Brotbox

 

Zusammenfassung

Die Einführung in das Boxing ist hiermit abgeschlossen. Das Schreiben und Anwenden von Funktionen, die einen generischen Objektparameter benötigen, wird durch die Verwendung von Verpackungen einfach und verständlich. Wie bei vielen nützlichen Dingen gibt es auch beim Verpacken das Problem des Overhead. Nächsten Monat werde ich den Overhead näher beschreiben und Möglichkeiten zur Reduzierung und zukünftige Erweiterungen von C# ansprechen, die uns das Leben einfacher machen werden.

 

C#-Websites

Dies sind die Websites, von denen ich per E-Mail erfahren habe. Das ist aber auch alles, was ich über sie weiß. Beschweren Sie sich also bitte nicht bei mir, wenn der Code nicht kompiliert wird, wenn Ihr Computer schmilzt oder wenn das Fell Ihres Hundes plötzlich kahle Stellen aufweist.
In diesem Monat habe ich nur zwei Einträge:

Eric Gunnerson ist der QS-Leiter des C#-Compilerteams, Mitglied des C#-Entwurfsteams und der Autor von "A Programmer's Introduction to C#" (in Englisch). Er hat bereits soviel Programmiererfahrung, dass er (noch) weiß, was 8-Zoll-Disketten sind, und er konnte Bänder früher einmal einhändig einlegen.

Arbeiten mit C# - Archivierte Kolumnen

How do I find X? How do I do Y? Does C# do Z? (in Englisch) 18. Januar
Fotos: Sean Masterton/Microsoft Corporation