299_titelei.fm Seite i Mittwoch, 20. November 2002 9:44 09
C# IN A NUTSHELL
299_titelei.fm Seite ii Mittwoch, 20. November 2002 9:44 09
299_titelei.fm Seite iii Mittwoch, 20. November 2002 9:44 09
C#
IN A NUTSHELL
Peter Drayton, Ben Albahari & Ted Neward
Deutsche Übersetzung von Dorothea Reder & Jürgen Key
Beijing · Cambridge · Farnham · Köln · Paris · Sebastopol · Taipei · Tokyo
299_titelei.fm Seite iv Mittwoch, 20. November 2002 9:44 09
Die Informationen in diesem Buch wurden mit größter Sorgfalt aufbereitet. Dennoch können Fehler nicht vollständig ausgeschlossen werden. Verlag, Autoren und Übersetzer übernehmen keine juristische Verantwortung oder irgendeine Haftung für eventuell verbliebene Fehler und deren Folgen. Alle Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt und sind möglicherweise eingetragene Warenzeichen. Der Verlag richtet sich im wesentlichen nach den Schreibweisen der Hersteller. Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen. Kommentare und Fragen können Sie gerne an uns richten: O’Reilly Verlag Balthasarstr. 81 50670 Köln Tel.: 0221/9731600 Fax: 0221/9731608 E-Mail:
[email protected] Copyright der deutschen Ausgabe: © 2003 by O’Reilly Verlag GmbH & Co. KG Die Originalausgabe erschien 2002 unter dem Titel C# in a Nutshell im Verlag O’Reilly & Associates, Inc.
Die Darstellung eines Jungfernkranichs im Zusammenhang mit dem Thema C# ist ein Warenzeichen von O’Reilly & Associates, Inc. Microsoft, .NET, Visual Basic .NET, Visual Studio .NET und Windows sind geschützte Warenzeichen der Microsoft Corporation.
Bibliografische Information Der Deutschen Bibliothek Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detailierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.
Übersetzung und deutsche Bearbeitung: Dorothea Reder, Bornheim & Jürgen Key, Ilmenau Lektorat: Alexandra Follenius & Lars Schulten, Köln Korrektorat: Friederike Daenecke, Zülpich Fachgutachten: Uwe Komoss, Kaarst, Christian Pogea, Ulm & Sven Riedel, Clausthal Satz: Frank Wassen, reemers publishing services, Krefeld; www.reemers.de Umschlaggestaltung: Emma Colby, Pam Spremulli & Melanie Wang, Boston Produktion: Geesche Kieckbusch, Köln Belichtung, Druck und buchbinderische Verarbeitung: Druckerei Kösel, Kempten; www.koeselbuch.de ISBN 3-89721-299-4 Dieses Buch ist auf 100% chlorfrei gebleichtem Papier gedruckt.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Inhalt
Vorwort .
xiii
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Teil I: Programmieren mit C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1: Einfhrung in C# und das .NET Framework
. . . Die Programmiersprache C# . . . . . . . . . . . . . . . . . . . . . . . . . Das .NET Framework . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ECMA-Standardisierung . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2: Grundlagen von C#
. . . Das erste C#-Programm . . . . . . Bezeichner und Schl%sselw&rter Grundlegendes zu den Typen . . Werttypen und Referenztypen . Vordefinierte Typen . . . . . . . . Arrays . . . . . . . . . . . . . . . . . Variablen und Parameter . . . . . Ausdr%cke und Operatoren . . . Anweisungen. . . . . . . . . . . . . Namensr7ume . . . . . . . . . . . .
3: Typ-Erzeugung in C# Klassen. . . . . . . . . . Vererbung. . . . . . . . Zugriffsmodifikatoren Structs . . . . . . . . . . Interfaces . . . . . . . . Enums . . . . . . . . . .
Inhalt
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . . .
. . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. 3 . 4 . 7 11
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
14
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
49
14 15 16 19 25 30 32 35 38 46
49 60 67 68 69 72
v
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
4: Fortgeschrittene C#-Features . Delegates. . . . . . . . . . . . . . . . . Delegates versus Funktionszeiger . Delegates versus Interfaces . . . . . Events . . . . . . . . . . . . . . . . . . . Operatoren %berladen . . . . . . . . Try-Anweisungen und Exceptions Attribute . . . . . . . . . . . . . . . . . Unsicherer Code und Zeiger . . . . Pr7prozessordirektiven . . . . . . . . XML-Dokumentation . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
75 . . . . . . . . . .
75 77 77 77 80 83 87 89 92 93
Teil II: Programmieren mit dem .NET Framework . . . . . 101 5: .berblick ber die Framework Class Library . Kerntypen . . . . . . . . . . . . . . . . . . . . Text . . . . . . . . . . . . . . . . . . . . . . . . Collections . . . . . . . . . . . . . . . . . . . . Streams und I/O . . . . . . . . . . . . . . . . Netzwerk-Programmierung . . . . . . . . . Threads . . . . . . . . . . . . . . . . . . . . . . Sicherheit. . . . . . . . . . . . . . . . . . . . . Reflection und Metadaten . . . . . . . . . . Assemblies . . . . . . . . . . . . . . . . . . . . Serialisierung . . . . . . . . . . . . . . . . . . Remoting . . . . . . . . . . . . . . . . . . . . . Web Services . . . . . . . . . . . . . . . . . . Datenzugriff . . . . . . . . . . . . . . . . . . . XML . . . . . . . . . . . . . . . . . . . . . . . . Grafik . . . . . . . . . . . . . . . . . . . . . . . Rich Client-Anwendungen . . . . . . . . . Web-basierte Anwendungen . . . . . . . . Lokalisierung (Globalization). . . . . . . . Konfiguration . . . . . . . . . . . . . . . . . . Advanced Component Services . . . . . . Diagnose und Debugging . . . . . . . . . . Zusammenarbeit mit unmanaged Code . Compiler- und Werkzeugunterst%tzung . Einrichten der Laufzeitumgebung . . . . . Native Betriebssystemf7higkeiten . . . . . Undokumentierte Typen. . . . . . . . . . .
6: String-Verarbeitung .
. . . . . . . Die Klasse String . . . . . . . . . . . . . . . Die Klasse StringBuilder . . . . . . . . . . Unterst%tzung f%r regul7re Ausdr%cke . Grundlagen regul7rer Ausdr%cke . . . .
vi
. . . . .
103
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
103 104 104 104 105 105 105 106 106 106 107 107 108 108 108 108 109 109 110 110 110 110 111 111 111 112
113 113 116 116 118
Inhalt
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Prozedurale und ausdrucksbasierte Muster . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Kochbuch f%r regul7re Ausdr%cke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
7: Collections .
. . . . . . . . . . . Iterieren %ber Collections. . . . . Standard-Collection-Interfaces . Vordefinierte Collection-Klassen Ordnen von Instanzen. . . . . . . Erzeugen von Hash-Werten . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
126
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . . . Zugriff auf XML-Dokumente . . . . . . . . . . . Parsen eines XML-Streams . . . . . . . . . . . . Knoten mit XPath ausw7hlen . . . . . . . . . . Transformieren eines Dokuments mit XSLT .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
. . . . . . . . . . . . . . . . . . . . . . Was ist Serialisierung? . . . . . . . . . . . . . . . . . . . . . Unterst%tzung der Serialisierung im .NET Framework Explizite Serialisierung . . . . . . . . . . . . . . . . . . . . Implizite Serialisierung . . . . . . . . . . . . . . . . . . . . [Serializable] . . . . . . . . . . . . . . . . . . . . . . . . . . . [NonSerialized]. . . . . . . . . . . . . . . . . . . . . . . . . . IDeserializationCallback . . . . . . . . . . . . . . . . . . . ISerializable. . . . . . . . . . . . . . . . . . . . . . . . . . . . [Serializable] und ISerializable . . . . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
8: XML-I/O .
9: Netzwerk-Programmierung
. . . . Programmiermodelle f%r Netzwerke . . . . . Request/Response-Architektur . . . . . . . . . HTTP-spezifische Unterst%tzung . . . . . . . . WebClient . . . . . . . . . . . . . . . . . . . . . . . Hinzuf%gen neuer Protokoll-Handler . . . . . Die Benutzung von TCP, UDP und Sockets . Die Benutzung des DNS. . . . . . . . . . . . . .
10: Streams und I/O
. . . . Streams und Speichermedien Kapseln roher Streams. . . . . Verzeichnisse und Dateien . . Isolierter Speicher. . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
11: Serialisierung .
12: Assemblies .
. . . . . . . . . . . . . . . . . . . . Elemente einer Assembly . . . . . . . . . . . . . . Assemblies und Module . . . . . . . . . . . . . . . Sichtbarkeit von Typen und Typ-Referenzen . Benennen und Signieren von Assemblies . . .
Inhalt
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
126 131 132 135 137
139 . . . .
139 143 143 146
149 . . . . . . .
149 149 150 151 151 152 153
154 . . . .
154 156 158 162
165 . . . . . . . . .
165 166 166 167 168 169 169 170 171
175 . . . .
175 176 176 177
vii
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Aufl&sen und Laden von Assemblies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 Deployment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Rechte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
13: Reflection
. . . . . . . . . . . . . . . . . . . . Typ-Hierarchie . . . . . . . . . . . . . . . . . . . Typen, Member und eingebettete Typen . . Den Typ einer Instanz ermitteln . . . . . . . . Direkter Zugriff auf einen Typ . . . . . . . . . Reflection %ber eine Typ-Hierarchie . . . . . Sp7te Bindung . . . . . . . . . . . . . . . . . . . Fortgeschrittene Benutzung von Reflection. Erzeugung neuer Typen zur Laufzeit . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . .
. . . . . . . . . . Sprachunterst%tzung . . . . . . . . . . . . . . . . . . . . . . Compiler-Unterst%tzung . . . . . . . . . . . . . . . . . . . . Laufzeitunterst%tzung . . . . . . . . . . . . . . . . . . . . . . Vordefinierte Attribute . . . . . . . . . . . . . . . . . . . . . Definieren eines neuen benutzerdefinierten Attributs . Abfragen eines benutzerdefinierten Typs zur Laufzeit.
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . .
. . . .
14: Benutzerdefinierte Attribute
15: Speichermanagement
. . . Der Garbage Collector . . . . . . . . Optimierungstechniken . . . . . . . Finalizer . . . . . . . . . . . . . . . . . Die Methoden Dispose und Close.
182 183 184 184 185 186 188 189
191 191 192 192 193 195 196
198
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. 203 . . 203 . . 206 . . 207
. . Aufrufe in DLLs . . . . . . . . . . . . . . . Das Marshaling einfacher Typen. . . . Marshaling von Klassen und Structs . In- und Out-Marshaling . . . . . . . . . Callbacks von unmanaged Code. . . . Simulieren einer C-Union . . . . . . . . Structs auf ein Bin7r-Format abbilden Vordefinierte Attribute f%r DLLs . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. Binden von COM- und C#-Objekten . . . . . . . . . . . COM-Objekte f%r C# zur Verf%gung stellen. . . . . . . C#-Objekte f%r COM zur Verf%gung stellen. . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. 220 . . 220 . . 220 . . 221
16: Threads .
. . . . . . . . . . Thread-Synchronisierung . Verbreitete Thread-Typen . Asynchrone Delegates . . .
. . . .
. . . .
. . . .
. . . .
. . . .
17: Integration nativer DLLs
18: Integration von COM-Komponenten .
viii
182
. . . . . . . . .
198 199 200 201
209 . . . . . . . .
209 210 211 212 212 213 215 216
Inhalt
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
COM-Abbildung in C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222 Attribute f%r die Zusammenarbeit mit COM. . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Unterst%tzung von COM+. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
19: Diagnose .
. . . . . . . . . . . . . . . . . . . Unterst%tzung f%r Logging und Assertions . Bedingte Kompilierung . . . . . . . . . . . . . Debugger-Integration . . . . . . . . . . . . . . Prozesse, Threads und Stacks . . . . . . . . . Event-Logs . . . . . . . . . . . . . . . . . . . . . Performance-Counter . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
226 . . . . . .
. . . . . .
226 228 229 230 232 235
Teil III: Sprach- und Tool-Referenz . . . . . . . . . . . . . . . . . . . . . . 239 20: C#-Sprachreferenz .
21: Referenz zu XML-Dokumentations-Tags . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
23: C#-Entwicklungswerkzeuge
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
249
. . . . . . . . . . . . . . . . . .
22: Namens- und Programmierkonventionen in C# . Groß-/Kleinschreibung Mechanismen . . . . . . Wortwahl . . . . . . . . . Namensr7ume . . . . . .
241
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
253 . . . .
. . . .
253 255 256 257
259
Teil IV: API-Schnellreferenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295 24: So verwenden Sie diese Schnellreferenz .
. . . . . . . . . . . . . . . . . . 297 Finden eines Eintrages in der Schnellreferenz . . . . . . . . . . . . . . . . . . . . . . . . . . 297 Wie Sie einen Eintrag der Schnellreferenz lesen sollten . . . . . . . . . . . . . . . . . . . . 298
25: Microsoft.Win32 26: System
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
303
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
313
27: System.Collections .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
436
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
445
28: System.Collections.Specialized 29: System.Diagnostics
Inhalt
419
ix
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
30: System.Globalization . 31: System.IO
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
487
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
508
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
538
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
544
32: System.IO.IsolatedStorage. 33: System.Net
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
575
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
589
34: System.Net.Sockets 35: System.Reflection
36: System.Reflection.Emit
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
661
. . . . . . . . . . . . . . . . . . . . . . . . . . .
690
37: System.Runtime.InteropServices . 38: System.Runtime.Serialization .
. . . . . . . . . . . . . . . . .
702
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
707
39: System.Runtime.Serialization.Formatters 40: System.Text.
. . . . . . . . . . . . . . . . . . . . . . . . .
715
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
723
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
743
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
746
41: System.Text.RegularExpressions . 42: System.Threading . 43: System.Timers . 44: System.Xml.
630
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
784
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
792
45: System.Xml.XPath 46: System.Xml.Xsl
Teil V: Anh@nge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 797 A: Regul@re Ausdrcke . B: Formatangaben
x
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
799
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
804
Inhalt
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
C: Marshaling von Daten . D: SchlsselwArter in C# .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
810
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
812
E: Namensr@ume und Assemblies F:
. . . . . . . . . . . . . . . . . . . . . . . . . .
Index zu den Typen, Methoden, Eigenschaften, Events und Feldern .
Index
Inhalt
817
. . . . . . . . . . . . . . . . . . . . . .
823
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
873
xi
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Vorwort Dieses Buch ist eine Schnellreferenz zu der neuen Programmiersprache C# von Microsoft. Es soll neben dem Computer und auf Reisen Ihr treuer Begleiter sein und Sie beim Programmieren unterst%tzen. Die erste Version von C# wurde nach einer umfangreichen Beta-Testphase im Januar 2002 als Teil von Visual Studio .NET und des .NET Framework ver&ffentlicht. C# in a Nutshell ist in vier Teile (und sechs Anh7nge) aufgeteilt. Teil I ist eine Einf%hrung in die Programmiersprache C# und die .NET Common Language Runtime (CLR). Ohne Umschweife werden Sie schnell mit der Programmiersprache C# vertraut gemacht, angefangen bei ihren Datentypen bis hin zu allen Anweisungen und Features, die diese moderne, komponentenorientierte Sprache auszeichnen. In Teil II sehen Sie, wie Sie C# zusammen mit den wichtigsten Klassen der .NET Framework Class Library (FCL) einsetzen, um eine Reihe von h7ufigen Programmieraufgaben zu l&sen, von der String-Verarbeitung bis hin zur Interaktion mit Legacy-COM-Komponenten. Teil III enth7lt einige n%tzliche Referenzen, darunter eine Zusammenfassung der Syntax von C#, die in der speziell f%r dieses Buch und C# Essentials entwickelten Notation vorgestellt wird, sowie eine Referenz zu n%tzlichen Kommandozeilen-Tools, die mit Visual Studio .NET und dem .NET Framework mitgeliefert werden. Teil IV ist eine Schnellreferenz zu den 21 wichtigsten Namensr7umen der FCL und ihren mehr als 700 Typen, komplett mit Namensraum-Karten, Typbeschreibungen, Member-Signaturen und n%tzlichen Querverweisen und Kommentaren. Oberdies bietet dieses Buch einen Index zu Klassen und Methoden, in dem Sie den Typ zu einem bestimmten Member oder den Namensraum zu einem bestimmten Typ nachschlagen k&nnen.
Zielgruppe dieses Buches Wenn Sie als C#-Programmierer t7tig sind, dann geh&rt C# in a Nutshell unabh7ngig von Ihrem gegenw7rtigen Erfahrungsstand auf jeden Fall in Ihre Referenzbibliothek. Die gr%ndliche Beschreibung der Sprachelemente, die kompakte Sprach- und Tool-Referenz und der ein-
Vorwort
xiii
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
zigartige, prall gef%llte Leitfaden durch die Core-APIs der FCL helfen Ihnen, m%helos Antworten auf die meisten Fragen zu Syntax und Funktionalit7t zu finden, denen Sie bei Ihrer Arbeit begegnen werden. Wenn Sie bereits Erfahrung in der Programmierung mit Java, C++ oder Visual Basic haben, aber zum erstenmal mit C# und der CLR zu tun haben, hilft Ihnen dieses Buch, die Grundlagen von C# rasch zu meistern. Unerfahrene Programmierer sollten jedoch vorher ein Einf%hrungsbuch lesen, z.B. Programmieren mit C# von Jesse Liberty (erschienen im O’Reilly Verlag).
Inhalt dieses Buches In den Teilen I, II und III dieses Buches werden C#, die .NET CLR und wichtige Tools beschrieben, die mit dem SDK zum .NET Framework heruntergeladen werden. Teil I, Programmieren mit C#, ist eine Einf%hrung in die Programmiersprache C# und das .NET Framework: Kapitel 1, Einf$hrung in C# und das .NET Framework Kapitel 1 gibt einen Oberblick %ber die Programmiersprache C# und das .NET Framework mit besonderem Schwerpunkt auf den wichtigsten Features und Vorteilen dieser Technologien. Kapitel 2, Grundlagen von C# In diesem Kapitel werden die Elemente der Programmiersprache C# eingef%hrt, darunter die Datentypen und elementare Konstrukte wie z.B. Ausdr%cke und Anweisungen. Im gesamten Kapitel wird eine Spezialnotation verwendet, um die Syntaxregeln von C# so zusammenzufassen, daß sie sp7ter leicht nachgeschlagen werden k&nnen. Kapitel 3, Typ-Erzeugung in C# In Kapitel 3 wird beschrieben, wie Sie in C# neue Typen definieren und instantiieren. In C# sind alle Klassen zugleich Komponenten, die sowohl den ausf%hrbaren Code als auch die Metadaten verk&rpern, die von der CLR zur Laufzeit verwendet werden. Kapitel 4, Fortgeschrittene C#-Features Dieses Kapitel erl7utert Features von C#, die f%r die Behandlung von Events und Exceptions, Callbacks, benutzerdefinierten Attributen und anderem mehr zust7ndig sind. Der Teil II, Programmieren mit dem .NET Framework, behandelt folgende Themen: Kapitel 5, /berblick $ber die Framework Class Library Kapitel 5 gibt einen Oberblick %ber die Kern-APIs des .NET Framework, soweit sie in diesem Buch behandelt werden. Die APIs selbst werden in Teil IV beschrieben. Im vorliegenden Kapitel wird zusammengefaßt, welche Unterst%tzung die einzelnen funktionalen Bereiche der FCL bieten. Außerdem sind hier die Namensr7ume aufgelistet, zu denen die jeweiligen Typen geh&ren. Kapitel 6, String-Verarbeitung In diesem Kapitel werden die Typen der FCL beschrieben, die f%r die String-Verarbeitung am wichtigsten sind, und es wird gezeigt, wie Sie diese Typen in C# einsetzen. Die FCL stellt eine breite Palette von Features f%r die fortgeschrittene String-Verarbeitung zur Verf%gung. Außerdem werden die FCL-Typen f%r das Suchen und Ersetzen mit regul7ren Ausdr%cken erkl7rt, die auf den regul7ren Ausdr%cken von Perl 5 beruhen.
xiv
Vorwort
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Kapitel 7, Collections Kapitel 7 stellt die wichtigsten FCL-Typen f%r die Arbeit mit gebr7uchlichen Datenstrukturen wie Arrays, Hashtables, W&rterb%chern, Stacks und dergleichen vor. Außerdem werden wichtige Collection-Interfaces wie IEnumerable, ICollection und IComparable beschrieben. Kapitel 8, XML-I/O In diesem Kapitel wird die integrierte XML-Unterst%tzung der FCL beschrieben, die Zusammenh7nge zwischen XML und dem I/O-System im allgemeinen erkl7rt, die Nutzung und Erstellung von XML-Dokumenten in knotenbasierten und Infoset-basierten Formularen behandelt sowie der Einsatz von XPath erl7utert und XSLT vorgestellt. Kapitel 9, Netzwerk-Programmierung Die FCL enth7lt eine Reihe von Typen, die den Zugriff auf Netzwerkressourcen vereinfachen. Kapitel 9 beschreibt den Kern der FCL-Netzwerkunterst%tzung und gibt eine Vielzahl von Beispielen f%r die Nutzung der vordefinierten Klassen. Kapitel 10, Streams und I/O Dieses Kapitel f%hrt in die integrierte FCL-Unterst%tzung f%r die Behandlung von Streams und I/O ein, erkl7rt die Zusammenh7nge zwischen den abstrakten und konkreten Klassen der Stream-Architektur, zeigt, wie diese Klassen eingesetzt werden, und erl7utert die spezifische Unterst%tzung f%r bestimmte Dateisysteme. Kapitel 11, Serialisierung Kapitel 11 gibt eine Einf%hrung in die integrierte Unterst%tzung f%r Objektserialisierung und -deserialisierung und zeigt, wie Clients und Objekte an dem Prozeß der Serialisierung und Deserialisierung teilnehmen k&nnen. Kapitel 12, Assemblies Dieses Kapitel erkl7rt die Konfiguration und Nutzung von Assemblies, der grundlegenden Einheit f%r die Weitergabe von Anwendungen in .NET. Ebenfalls erl7utert werden die FCL-Attribute und -Typen zur Verwaltung von Assemblies. Kapitel 13, Reflection Kapitel 13 beschreibt die wichtigsten FCL-Typen zur Untersuchung der Metadaten vorhandener Typen mit Hilfe von Reflection. Die Erstellung neuer Typen (und der damit verbundenen Metadaten) nennt sich Reflection.Emit und wird mit den Typen aus dem Namensraum System.Reflection.Emit vorgenommen, der ebenfalls in diesem Kapitel behandelt wird. Kapitel 14, Benutzerdefinierte Attribute Typen, Member, Module und Assemblies haben allesamt Metadaten, die von allen wichtigen CLR-Diensten genutzt werden. Diese Metadaten gelten als integraler Bestandteil einer Anwendung und lassen sich mittels Reflection (siehe Kapitel 13, Reflection) betrachten. Dieses Kapitel erkl7rt, wie Sie Anwendungselementen benutzerdefinierte Metadaten hinzuf%gen, indem Sie eigene Attribute entwerfen. Kapitel 15, Speichermanagement Das .NET Framework bietet eine automatische Garbage Collection f%r Typen, die nicht mehr benutzt werden, und erm&glicht es den Programmierern, %ber C#-Destruktoren auch eigene Finalizer zur Verf%gung zu stellen. Außerdem zeigt dieses Kapitel, wie Sie mit Dispose()- oder Close()-Methoden hinter einem Objekt aufr7umen, dessen Arbeit beendet ist.
Vorwort
xv
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Kapitel 16, Threads Kapitel 16 erkl7rt die Verwendung der FCL-Typen zur Verwaltung von AnwendungsThreads. In C# gibt es eine lock-Anweisung, mit der Sie den Zugriff auf gemeinsame Ressourcen synchronisieren; und in der FCL gibt es die Klasse Monitor, mit der Sie pulse- und wait-, atomic- und andere Thread-Operationen implementieren. Kapitel 17, Integration nativer DLLs Dieses Kapitel erkl7rt die PInvoke-Dienste, %ber die C#-Programme mit Legacy-DLLs interagieren. Kapitel 18, Integration von COM-Komponenten Kapitel 18 beschreibt die FCL-Typen und -Attribute, mit denen COM-Objekte f%r C#-Programme und C#-Objekte f%r COM verf%gbar werden. Kapitel 19, Diagnose Da alle Anwendungen eine integrierte Fehlerbehandlung und -meldung erfordern, bietet das .NET Framework etliche Funktionen, mit denen Sie das Verhalten von Anwendungen %berwachen, Laufzeitfehler entdecken, die Anwendungsumgebung untersuchen, Meldungen %ber den Status der Anwendung erhalten und eine Integration der vorhandenen Debugging-Tools bewerkstelligen k&nnen. Dieses Kapitel f%hrt in die Debugging- und Diagnoseunterst%tzung der FCL ein. Teil III, Sprach- und Tool-Referenz, behandelt folgende Themen: Kapitel 20, C#-Sprachreferenz Kapitel 20 enth7lt eine kurze alphabetische Auflistung aller Sprachkonstrukte von C# und ihrer jeweiligen Syntax. Kapitel 21, Referenz zu XML-Dokumentations-Tags C# stellt XML-Dokumentations-Tags zur Verf%gung, die es Ihnen leichtmachen, eine Anwendungsdokumentation direkt mit dem Quellcode zu erstellen. In diesem Kapitel werden die entsprechenden Tags sowie ihre Verwendung vorgestellt. Kapitel 22, Namens- und Programmierkonventionen in C# Kapitel 22 schl7gt Richtlinien f%r die Namensgebung sowie f%r die Groß- und Kleinschreibung von C#-Programmierkonstrukten vor. Diese Richtlinien wurden anhand der offiziellen Microsoft-Dokumente und der Erfahrungen der Autoren dieses Buchs erarbeitet. Kapitel 23, C#-Entwicklungswerkzeuge Dieses Kapitel ist eine Referenz zu n%tzlichen Kommandozeilen-Tools, die mit Visual Studio .NET und dem .NET Framework ausgeliefert werden. Dazu geh&ren auch der Compiler und der Debugger. Die 23 Kapitel der Teile I bis III von C# in a Nutshell machen Sie mit C# und vielen wichtigen APIs des .NET Framework vertraut. Die zweite H7lfte des Buchs ist der Teil IV, die APISchnellreferenz. Diese ist eine knappe, aber detaillierte API-Referenz zu 21 wichtigen Namensr7umen und mehr als 700 Kerntypen einschließlich ihrer Member. Bitte lesen Sie hierzu auf jeden Fall Kapitel 24, So verwenden Sie diese Schnellreferenz, am Anfang von Teil IV, in dem erkl7rt wird, wie Sie aus der Referenz den gr&ßtm&glichen Nutzen ziehen. Teil V, Anh?nge, enth7lt zus7tzliche Referenztabellen, darunter die Syntax von regul7ren Ausdr%cken (Anhang A, Regul?re Ausdr$cke), Formatangaben (Anhang B, Formatangaben), standardm7ßige Datenzuordnung von C# zu COM (Anhang C, Marshaling von Daten), ein Glossar
xvi
Vorwort
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
der C#-Schl%sselw&rter (Anhang D, Schl$sselw@rter in C#), eine alphabetische Liste der .NETNamensr7ume und der DLLs, die diese Namensr7ume enthalten (Anhang E, Namensr?ume und Assemblies), sowie einen Index der Typen und Member, in dem Sie Methoden oder Felder nachschlagen k&nnen, um zu sehen, mit welchem Typ sie definiert sind.
Voraussetzungen fr dieses Buch Um mit C# zu programmieren, m%ssen Sie zun7chst eine der vielen Editionen von Visual Studio .NET auf Ihrem System installiert haben (Standard, Professional, Enterprise oder Architect) oder das .NET Framework SDK heruntergeladen und eingerichtet haben. Visual Studio stellt eine umfangreiche interaktive Entwicklungsumgebung zur Verf%gung, die viele Aufgaben der C#-Programmierung erleichtert, vor allem die Entwicklung von Windows-Clients, serverseitigen Webanwendungen und Web Services. Außerdem bietet Visual Studio eine hervorragende Unterst%tzung f%r das Debugging und das Projektmanagment von Anwendungen. Wenn Sie das .NET Framework SDK auf Ihrem System installiert haben, k&nnen Sie allerdings auch mit einem einfachen Editor wie z.B. emacs, SharpDevelop oder Microsoft Notepad die C#-Programme schreiben und sie mit den in Kapitel 23, C#-Entwicklungswerkzeuge, beschriebenen Kommandozeilen-Tools debuggen und ausf%hren. Um das .NET Framework SDK (gegenw7rtig eine 131-MByte-Datei) herunterzuladen, gehen Sie zu http://msdn.microsoft.com/net. Wenn dieses Buch in die Regale der Buchh7ndler gelangt, d%rfte es jedoch auch bereits m&glich sein, das .NET Framework SDK auf CD zu erwerben.
Konventionen In diesem Buch gelten die folgenden typographischen Konventionen. Kursiv wird verwendet f%r:
· · · · · · · ·
Verzeichnispfade und Dateinamen Domain-Namen und URLs Neue Begriffe, die definiert werden
Nichtproportionalschrift wird verwendet f%r:
Codebeispiele und Ausgaben Namen und Schl%sselw&rter in C#-Programmen, einschließlich Methoden- oder Feldnamen, Variablennamen und Klassennamen XML- und HTML-Element-Tags Registry-Schl%ssel
Nichtproportionalschrift kursiv wird verwendet f%r:
Parameternamen oder Platzhalter, die im Programm durch einen tats7chlichen Wert ersetzt werden
Vorwort
xvii
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
An mehreren Stellen in diesem Buch finden Sie einfache Grammatikangaben zu vielen, aber nicht allen in diesem Buch eingef%hrten Sprachkonstrukten. So k&nnen Sie die Grammatik eines bestimmten Konstrukts und seine zul7ssigen Kombinationsm&glichkeiten schnell %berblicken. In der Syntax dieser Grammatik wird mit den XML-H7ufigkeitsoperatoren (?, * und +) konkreter angegeben, wie oft ein Element in einem bestimmten Konstrukt auftreten kann: x x
Zeigt an, daß x w&rtlich verwendet werden soll (Nichtproportionalschrift). Zeigt an, daß der Programmierer f%r x einen entsprechenden Wert einsetzt (Nichtproportionalschrift Kursiv).
x?
Zeigt an, daß x null oder einmal auftreten kann.
x*
Zeigt an, daß x null Mal oder mehrmals (durch Kommata getrennt) auftreten kann.
x+
Zeigt an, daß x einmal oder mehrmals (durch Kommata getrennt) auftreten kann.
[...]
Zeigt eine logische Zusammenfassung von Codeelementen an, soweit diese nicht implizit mit {}, () oder [] zusammengefaßt sind. [x|y]
Zeigt an, daß nur ein Element aus einer Auswahl von Codeelementen auftreten darf.
Dieses Icon zeigt einen Hinweis an, also eine wichtige Nebenbemerkung zum Text.
Weitere Literatur zu diesem Thema O’Reilly & Associates und der O’Reilly Verlag verlegen eine ganze Reihe von B%chern %ber C# und .NET, darunter auch mehrere erg7nzende B%cher zu dem vorliegenden Band. Empfehlenswert sind aus der .NET-Reihe von O’Reilly unter anderem:
· · · · · ·
Programmieren mit C# von Jesse Liberty .NET Framework Essentials, Second Edition, von Thuan Thai und Hoang Q. Lam VB.NET Language in a Nutshell, Second Edition, von Steven Roman, Ron Petrusha und Paul Lomax Programming Visual Basic .NET von Dave Grundgeiger Programming ASP.NET von Jesse Liberty und Dan Hurwitz C# and VB.NET Conversion Pocket Reference von Jose Mojica
Eine vollst7ndige Liste der .NET-B%cher von O’Reilly und anderer Titel zu Programmierthemen finden Sie unter http://dotnet.oreilly.com. Außerdem empfehlen wir C# Primer: A Practical Approach, von Stanley B. Lippman (Addison-Wesley Professional) und Effektiv Java programmieren von Joshua Bloch (auch f%r C#-Programmierer exzellent geeignet).
xviii
Vorwort
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Online-Ressourcen zu C# Auch dieses Buch kann nicht alle Fragen beantworten, die Sie zu C# vielleicht haben. Es gibt viele Online-Ressourcen, die Ihnen helfen, das Beste aus C# herauszuholen. Folgende Websites sind zu empfehlen: http://msdn.microsoft.com/net Das Microsoft .NET Developer Center ist die offizielle Website f%r alles, was mit .NET zu tun hat, einschließlich der neuesten Version des .NET Framework SDK, der Dokumentation, technischer Fachartikel, Beispielcode, Hinweise auf Diskussionsgruppen und Ressourcen von Fremdherstellern. http://www.gotdotnet.com Die Support-Seite des .NET Framework-Teams f%r die Programmierergemeinde. Hier finden Sie Artikel, Spezifikationen und viele Beispiele, sowohl aus dem .NET Framework-Team als auch von der Programmierergemeinde insgesamt. http://discuss.develop.com/dotnet.html Die Diskussionsliste von DevelopMentor DOTNET ist der beste Ort f%r eine unabh7ngige, offene Diskussion %ber das .NET Framework. Teilnehmer sind nicht selten hochstehende Ingenieure und Programm-Manager von Microsoft. http://www.ecma.ch/ecma1/stand/ECMA-334.htm http://www.ecma.ch/ecma1/stand/ECMA-335.htm Die offiziellen ECMA-Spezifikationen zu C# (ECMA-334) und CLI (ECMA-335). http://www.ondotnet.com Das .NET DevCenter im O’Reilly Network, mit Originalartikeln, News und interessanten Weblogs f%r .NET-Programmierer. http://dotnet.oreilly.com Das O’Reilly .NET-Center. Schauen Sie &fters hier vorbei, wenn Sie Informationen %ber Neuerscheinungen von O’Reilly ben&tigen. Hier finden Sie Beispielkapitel, Artikel und andere Ressourcen. http://msdn.microsoft.com/community/net.asp Eine Liste von Ressourcen der Fremdhersteller f%r C#- und .NET Framework-Entwickler. Es gibt auch im Usenet Diskussionen %ber .NET, und zwar in der Newsgruppenfamilie microsoft.public.dotnet.* Außerdem k%mmert sich die Newsgruppe microsoft.public.dotnet.languages.csharp speziell um C#. Wenn Ihr News-Server diese Gruppen nicht anbietet, finden Sie sie unter news://msnews.microsoft.com. Abschließend seien noch zwei interessante Artikel erw7hnt: http://windows.oreilly.com/news/hejlsberg_0800.html Ein Interview mit dem Erfinder von C#, Anders Hejlsberg, vom O’Reilly-Editor John Osborn. http://www.genamics.com/developer/csharp_comparative.htm Ein Vergleich zwischen C# und C++ sowie Java vom Co-Autor dieses Buchs, Ben Albahari.
Vorwort
xix
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
So wurde die Schnellreferenz generiert Teil IV, die API-Schnellreferenz, wurde mit Hilfe der Reflection-API von .NET generiert (die in Kapitel 13, Reflection, genauer beschrieben ist). Mit Reflection nahmen wir bestimmte Klassen, Structs, Enums, Delegates und Interfaces der Framework Class Library unter die Lupe und extrahierten detaillierte Informationen %ber jeden Typ und seine Member. Danach strukturierten wir diese Informationen als DocBook XML, und aus diesem Format haben wir die Druckversion erstellt. Jedes angegebene Typlisting wird von einem oder mehreren Abs7tzen begleitet, die den Typ und seine wichtigsten Member beschreiben. Diese Beschreibungen geben einen Oberblick dar%ber, wie Sie den Typ in Ihren eigenen Anwendungen nutzen k&nnen, und beschreiben verwandte Typen, Fallstricke und n%tzliche Features.
Danksagungen Ohne die Mithilfe vieler Beteiligter, einschließlich der Familien, der Freunde und der hart arbeitenden Mitarbeiter von O’Reilly w7re dieses Buch niemals erschienen. Brian Jepson und Lenny Muellner schrieben die Programme, mit denen Teil IV und der Anhang F generiert wurden. Brian entwickelte außerdem unter Mithilfe von Ted Neward und Peter Drayton die Namensraum-Karten, die Sie am Anfang jedes Referenzkapitels als Oberblicksdarstellung finden. Peter Drayton schrieb das Programm, mit dem das Material f%r Anhang E, Namensr?ume und Assemblies, generiert wurde. Ted Neward, Matthew MacDonald, Martin Smith, Steve Spainhour und Brian Jepson erstellten die mehr als 700 Namensraum- und Typbeschreibungen, durch die die API-Referenz so wertvoll wird. Brad Merrill von Microsoft schrieb die Abschnitte %ber regul7re Ausdr%cke und lieferte den Inhalt f%r Anhang A, Regul?re Ausdr$cke. Brad Abrams und Mitarbeiter aus seinem .NET Framework-Team trugen zum Entwurf der APIReferenz bei und verbesserten sie durch ihr Fachlektorat ganz entscheident. Weitere technische Gutachter waren Joe Nalewabau und Kerry Loynd von Microsoft.
Peter Drayton In erster Linie danke ich meiner Frau Julie DuBois, die es immer wieder schafft, daß die Tage, die ich ohne .NET verbringe, sogar noch sch&ner sind als die, die ich mit .NET verbringe. Außerdem danke ich Ben Albahari daf%r, daß er den Materialberg zu C# und .NET schon zum zweiten Mal in weniger als anderthalb Jahren mit mir bew7ltigt hat; ich danke Ted Neward, der als Kavallerie immer zur rechten Zeit kam und aus der zweiten H7lfte des Buchs das Bestm&gliche machte, und Brian Jepson, Nancy Kotary sowie John Osborn f%r ihre unerm%dliche Hilfe und Arbeit (sowohl als Verleger als auch in anderer Hinsicht). Wenn man den Bogen noch etwas weiter spannt, so wurden die Beitr7ge in diesem Buch auch durch Kommentare und Anregungen von Simon Fell, Don Box, Brock Allen und all den anderen klugen und talentierten DevelopMentor-Lehrern, deren Kollege zu sein ich das Gl%ck habe, um ein Vielfaches besser. Ich widme dieses Buch meinem Vater Peter Drayton Senior und meiner Mutter Irene Mary Rochford Drayton, die mir alles mitgaben, um durch die F7hrnisse des Lebens zu steuern.
xx
Vorwort
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Ben Albahari Ich danke zun7chst Peter Drayton, der mich bat, an diesem Buch mitzuarbeiten, und der mich regelm7ßig mit seiner Begeisterung mitzieht; den Lektoren von O’Reilly (Brian Jepson, Nancy Kotary und John Osborn) f%r ihren Einsatz und die Geduld, die notwendig war, um dieses Projekt zum Abschluß zu bringen; Ted Neward, der zu uns stieß, als wir den schwierigsten Teil des Buchs in Angriff nahmen; und meiner Freundin Karen, die mich mit einer Mischung aus Liebe und Logik motiviert hat.
Ted Neward Zuallererst danke ich Peter Drayton, der mich einlud (oder n&tigte?), bei diesem Projekt mitzuarbeiten. Es hat Spaß gemacht, mit ihm und Ben Albahari an einem Buch zu arbeiten, das, wie ich hoffe, f%r Tausende von abtr%nnigen C++-, Java- und VB-Programmierern, die zu dieser neuen Sprache wechseln, von Nutzen sein wird. Zweitens danke ich dem Team von O’Reilly, das meine erste Zusammenarbeit mit O’Reilly zu einer wunderbaren Erfahrung machte. Drittens gilt mein Dank meinen jetzigen und ehemaligen Studenten, die meine Versuche, ihnen C# beizubringen, mit Fragen, Diskussionen und Lachen (vielleicht etwas hysterischem Lachen) begleiteten. Viertens danke ich Don Box und Mike Abercrombie f%r DevelopMentor und die herrliche Arbeitsatmosph7re, die sie dort schufen und an der ich teilhaben durfte. Und last but not least danke ich meiner wunderbaren Familie: Meiner Frau Charlotte und meinen S&hnen Michael und Matthew, die ihren Papa gern haben, auch wenn er manchmal f%r eine Weile im Arbeitszimmer verschwindet. Auch Ihnen, werter Leser, danke ich, da Sie sich die Zeit nehmen, unser Buch im Rahmen Ihrer Besch7ftigung mit C# zu lesen. Ich hoffe, daß Ihre Bem%hungen insofern Fr%chte tragen, als Sie durch unser Buch schneller lernen und mehr Zeit mit Ihrer Familie und Ihren Freunden verbringen k&nnen. Auch wenn es banal klingen mag: In dieser »neuen Zeit«, in der wir leben, sind solche Kontakte mit Gold nicht aufzuwiegen. Und deshalb gehe ich jetzt mit meiner Familie Pizza essen.
Vorwort
xxi
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
TEIL I
Programmieren mit C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Einfhrung in C#
KAPITEL 1
Einfhrung in C# und das .NET Framework Die neue Programmiersprache C# wurde von Microsoft speziell fr das .NET Framework entwickelt. Das .NET Framework ist eine Laufzeitumgebung und Klassenbibliothek, die die Entwicklung und den Einsatz moderner, komponentenbasierter Anwendungen massiv erleichtert. Als im Januar 2002 die endgltige Version des .NET Framework und der Programmiersprache C# ausgeliefert wurde, hatten sowohl die Plattform als auch die Programmiersprache bereits die Aufmerksamkeit der Industrie auf sich gezogen und wurden von der Avantgarde der Microsoft-Nutzer auf breiter Front eingesetzt. Wie kommt es zu diesem Erfolg? Gewiß, C# und das .NET Framework l0sen viele technische Probleme, mit denen Entwickler heutzutage konfrontiert sind, wenn sie versuchen, immer komplexere verteilte Systeme in immer krzerer Zeit mit immer weniger Teamkollegen zu erstellen. Neben den technischen Vorzgen ist jedoch der Hauptgrund dafr, daß C# und das .NET Framework so beliebt sind, die bisher beispiellose Offenheit, die Microsoft an den Tag legt. Von Juli 2000 bis Januar 2002 wurde das .NET Framework 0ffentlich umfangreich beta-getestet, so daß Zehntausende von Entwicklern die neue Entwicklungsumgebung einer »Nagelprobe« unterziehen konnten. So konnte Microsoft von den Erfahrungen und dem Feedback der Entwicklergemeinschaft profitieren, ehe die neue Plattform endgltig fertiggestellt wurde. Außerdem wurden die wichtigsten Spezifikationen fr die Programmiersprache und die Plattform von der internationalen Standardisierungsorganisation European Computer Manufacturers Association (ECMA) ver0ffentlicht, begutachtet und abgesegnet. Diese Standardisierungsbemhungen veranlaßten viele andere Hersteller, C# und die .NET-Plattform auf andere als Microsoft-Umgebungen zu portieren. Auch im akademischen Bereich ist das Interesse an Microsoft-Technologien fr Forschung und Lehre wieder erwacht. Obwohl C# und .NET zun?chst ganz neu aussehen, beruhen sie auf der soliden Grundlage einer langj?hrigen Entwicklungsarbeit. Wenn Sie wissen, wo die Sprache und die Plattform herkommen, dann verstehen Sie besser, wohin die Reise geht.
Kapitel 1: Einf hrung in C# und das .NET Framework
3
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Die Programmiersprache C# Die ersten Berichte ber eine neue Programmiersprache von Microsoft stammen aus dem Jahr 1998. Seinerzeit hieß die Sprache noch COOL und galt als sehr Java-?hnlich. Obwohl Microsoft hartn?ckig dementierte, eine neue Sprache zu entwickeln, hielten sich die Gerchte. Im Juni 2000 beendete Microsoft die Spekulationen und ver0ffentlichte die Spezifikationen fr eine neue Programmiersprache namens C# (englisch »ssie-sharp« ausgesprochen). Kurz danach wurde die Vorabversion des .NET Framework SDK ver0ffentlicht (die auch einen C#Compiler enthielt). Dies geschah auf der July 2000 Professional Developer’s Conference (PDC) in Orlando, Florida. Die Sch0pfer der neuen Sprache waren Anders Hejlsberg (der Entwickler von Turbo Pascal und Architekt von Delphi), Scott Wiltamuth und Peter Golde. Die C# Language Specification beschreibt C# als eine »...einfache, moderne, objektorientierte und typsichere Programmiersprache, die von C und C++ abstammt«. Die Syntax von C# hat viel Ehnlichkeit mit der Syntax von C++ und Java. Betrachtete man nur die syntaktischen Fbereinstimmungen zwischen C# und Java, so wrde man C# jedoch Unrecht tun. In der Semantik treibt C# den Sprachentwurf weit ber das hinaus, was Java im Jahre 2001 darstellte, und kann zu Recht als der n?chste Schritt in der Entwicklung komponentenorientierter Programmiersprachen betrachtet werden. Ein detaillierter Vergleich zwischen C# und Java wrde zwar den Rahmen dieses Buches sprengen, aber interessierten Lesern sei die Lektre des vielzitierten Artikels »A Comparative Overview of C# and Java« von Ben Albahari, dem Co-Autor dieses Buchs, unter http://genamics.com/developer/ csharp_comparative.htm empfohlen.
Komponentenbasierte Software-Entwicklung In den letzten zehn Jahren haben Programmiertechniken wie objektorientiertes Design, die Interface-basierte Programmierung und komponentenbasierte Software berall Einzug gehalten. Doch die Untersttzung, die Programmiersprachen fr solche Techniken boten, hinkte dem aktuellen Stand der Dinge immer hinterher. Daher sttzen sich die Programmierer entweder auf Programmierkonventionen und selbstgeschriebenen Code statt auf eine unmittelbare Untersttzung durch den Compiler und die Laufzeitumgebung, oder sie nutzen die modernen Techniken berhaupt nicht. C++ beispielsweise untersttzte zwar die Objektorientierung, hatte aber kein formales Konzept von Interfaces. So mußten die C++-Entwickler auf abstrakte Basisklassen und Mixin-Interfaces zurckgreifen, um eine Interface-basierte Programmierung zu simulieren, und auf externe Modelle der Komponentenprogrammierung wie z.B. COM oder CORBA, um die Vorteile komponentenbasierter Software nutzen zu k0nnen. Java erweiterte zwar den Ansatz von C++ unter anderem um eine Untersttzung von Interfaces und Packages, bot aber auf der Sprachebene keine ausreichende Untersttzung fr die Erstellung langlebiger, komponentenbasierter Systeme. (In solchen Systemen mssen ber lange Zeitr?ume hinweg Komponenten aus unterschiedlichen Quellen entwickelt, miteinander verbunden, weitergegeben und versioniert werden.) Nicht daß die Java-Gemeinschaft nicht viele solcher Systeme geschaffen h?tte, aber die entsprechenden Bedrfnisse wurden nicht auf der Sprachebene, sondern mittels Konventionen und selbstgeschriebenem Code gedeckt: Man sttzte sich auf Namenskonventionen, um gebr?uchliche Entwurfsmuster wie z.B. Eigenschaf-
4
Kapitel 1: Einf hrung in C# und das .NET Framework
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Bei der Entwicklung von C# hingegen stand von Anfang an die Tatsache im Mittelpunkt, daß moderne Systeme aus Komponenten errichtet werden. Daher bietet C# eine unmittelbare Sprachuntersttzung fr gebr?uchliche Komponentenkonstrukte wie z.B. Eigenschaften, Methoden und Events (die von RAD-Tools dazu genutzt werden, um Anwendungen aus Komponenten aufzubauen, Eigenschaften zu setzen, auf Events zu reagieren und die Komponenten durch Methodenaufrufe miteinander zu verbinden). Außerdem k0nnen Entwickler mit C# die Typinformationen einer Komponente unmittelbar kommentieren und erweitern, um eine Weitergabe-, Entwurfs- oder Laufzeituntersttzung zu gew?hrleisten, die Versionierung der Komponenten direkt in das Programmiermodell zu integrieren oder eine XML-Dokumentation unmittelbar in die C#-Quelldateien einzubinden. Darber hinaus macht C# Schluß mit der Unart von C++ und COM, Quelldateien ber die Header-Dateien, Implementierungsdateien und Typbibliotheken zu verteilen, und verwendet statt dessen ein viel einfacheres Modell zur Organisation des Quellcodes und fr die Wiederverwendung der Komponenten. Und dies sind noch nicht alle Vorteile von C#, das sich durch seine Verbesserungen gegenber Java und C++ als neuer Meilenstein in der Entwicklung komponentenbasierter Sprachen auszeichnet.
Eine moderne objektorientierte Sprache C# bietet nicht nur eine integrierte Untersttzung fr die Erstellung komponentenbasierter Systeme, sondern ist eine leistungsf?hige objektorientierte Sprache. Es untersttzt alle wichtigen Konzepte und Abstraktionen, die auch in Sprachen wie C++ und Java vorhanden sind. Wie man es von jeder modernen objektorientierten Sprache erwarten kann, untersttzt C# auch Vererbung, Kapselung, Polymorphismus, Interface-basierte Programmierung und gebr?uchliche Konstrukte der Programmiersprachen C, C++ und Java, darunter Klassen, Structs, Interfaces, Enums sowie neuere Konstrukte wie Delegates, die ein typsicheres Equivalent zu den Funktionszeigern von C und C++ darstellen, sowie benutzerdefinierte Attribute, mit denen Sie Codeelementen Zusatzinformationen mitgeben k0nnen. Fberdies hat C# auch Funktionen von C++, die in Java noch fehlen, darunter das Fberladen von Operatoren, benutzerdefinierte Konvertierungen, echte rechteckige Arrays und eine Semantik fr die Parameterbergabe by value. Im Gegensatz zu den meisten anderen Programmiersprachen hat C# keine eigene Laufzeitbibliothek, sondern sttzt sich auf die gewaltige Klassenbibliothek des .NET Framework, die fr alle Bedrfnisse das Richtige bietet, einschließlich Konsolen-I/O, Umgang mit Netzwerken und Dateien, Collection-Datenstrukturen und vieles andere. Diese Klassenbibliothek, die haupts?chlich in C# geschrieben wurde und mehr als eine Million Codezeilen umfaßt, war fr den Entwicklungszyklus der Sprache C# und des C#-Compilers der ultimative H?rtetest. C# bemht sich, Konsistenz und Effizienz gegeneinander abzuw?gen. Manche objektorientierten Sprachen (darunter Smalltalk) vertreten den Standpunkt, alles sei ein Objekt. Dieser Ansatz hat den Vorteil, daß Instanzen von Grundtypen (z.B. Integer) Objekte erster Klasse sind, aber er hat auch den Nachteil, sehr ineffizient zu sein. Um unn0tigen Aufwand zu vermeiden, teilen
Die Programmiersprache C#
5
Einfhrung in C#
ten und Events zu identifizieren, man ben0tigte fr die Weitergabe-Informationen externe Metadaten, und man entwickelte benutzerdefinierte Class Loader, um eine strengere Versionierungssemantik fr die Komponenten zu erzielen.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
andere Sprachen (darunter Java) das Typensystem in Grundtypen und andere Typen ein. Das bedeutet zwar weniger Aufwand, fhrt aber auch zu einer Spaltung zwischen elementaren und benutzerdefinierten Typen. C# vers0hnt beide Positionen, indem es ein einheitliches Typensystem pr?sentiert, in dem alle Typen (einschließlich der Grundtypen) von einem gemeinsamen Basistyp abgeleitet sind. Es erm0glicht gleichzeitig Leistungsoptimierungen, durch die Grundtypen und einfache benutzerdefinierte Typen als einfache Speicheradressen behandelt werden k0nnen. Das mindert den Aufwand und erh0ht die Effizienz.
Robuste und langlebige Software In einer Welt der Standleitungen und verteilten Systeme kommt der Robustheit von Software immer mehr Bedeutung zu. Server mssen 24 Stunden am Tag und sieben Tage in der Woche fr Clients zur Verfgung stehen, und Clients mssen dazu in der Lage sein, Code aus dem Netz herunterzuladen und lokal auszufhren, ohne ein Fehlverhalten befrchten zu mssen. C# (in Verbindung mit dem .NET Framework) f0rdert die Robustheit von Software auf mehreren unterschiedlichen Ebenen. Vor allem ist C# eine typsichere Sprache. Das bedeutet, daß Programme nur in korrekter Weise auf Objekte zugreifen k0nnen. Aller Code und alle Daten sind mit einem Typ verbunden, jedes Objekt hat einen zugeh0rigen Typ, und nur die Operationen, die von diesem Typ definiert werden, k0nnen auf dem Objekt ausgefhrt werden. Durch die Typsicherheit f?llt eine komplette Kategorie von Fehlern weg, die in C und C++ aufgrund von unzul?ssigen Typumwandlungen, falscher Zeigerarithmetik und sogar auch b0sartigem Code auftraten. Außerdem bietet C# ein automatisches Speichermanagement in Form eines HochleistungsGarbage Collectors, der generationsbasiert und Tracing-f?hig ist. Dieser erspart den Programmierern die manuelle Speicherverwaltung und das Z?hlen von Referenzen und beseitigt ebenfalls eine ganze Fehlerkategorie, darunter h?ngende Zeiger, Speicherlecks und zirkul?re Referenzen. Auch in den besten Programmen k0nnen schlimme Dinge passieren. Dies macht einen konsequenten Mechanismus zum Auffinden von Fehlern so wichtig. Fber die Jahre hinweg mußten sich die Windows-Entwickler mit einer Vielzahl von Fehlermeldungsmechanismen herumschlagen: Es gab einfache Rckgabecodes, die strukturierten Exceptions von Win32, die C++Exceptions, die COM-Fehler-HResults und die OLE-Automatisierung mit IErrorInfo-Objekten. Dieser Wildwuchs macht alles nur komplizierter und erschwert die Entwicklung standardisierter Verfahren zur Fehlerbehandlung. Das .NET Framework macht Schluß mit diesem Wildwuchs, indem es einen einzigen Exception-Handling-Mechanismus fr das gesamte Framework standardisiert und in allen .NET-Sprachen einschließlich C# zur Verfgung stellt. Außerdem bietet das C#-Sprachdesign eine Vielzahl weiterer Features zur F0rderung der Robustheit, darunter eine Sprachuntersttzung fr die unabh?ngige Versionierung von Basisklassen (ohne die Semantik abgeleiteter Klassen zu ?ndern oder eine erneute Kompilierung der abgeleiteten Klassen erforderlich zu machen), fr die Aufdeckung des Versuchs, nichtinitialisierte Variablen zu verwenden, fr die Prfung von Array-Grenzen und fr arithmetische Fberprfungen.
6
Kapitel 1: Einf hrung in C# und das .NET Framework
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Eine pragmatische Sicht der Dinge
Obwohl C# viele ntzliche objektorientierte Features auf hohem Niveau bietet, erkennt es auch an, daß diese Features in seltenen F?llen zu Lasten der Performance gehen k0nnen. Diese Bedenken werden nicht als unwichtig abgetan. C# bietet eine Untersttzung fr solche Features wie direkte Zeigerbearbeitung, unsichere Typumwandlungen, deklarative Fixierung von Objekten, die von der Garbage Collection erfaßt wurden, und direkte Speicherzuweisung im Stack. Natrlich haben diese Features auch ihren Preis, denn sie machen die Dinge komplizierter und drfen nur von Benutzern mit besonderen Sicherheitsberechtigungen eingesetzt werden. Dennoch gibt ihre Existenz den C#-Programmierern viel mehr Handlungsspielraum, als andere, restriktivere Sprachen gew?hren. Schließlich erleichtern auch die F?higkeiten zur Interoperabilit?t im .NET Framework eine Nutzung vorhandener DLLs und COM-Komponenten mit C#-Code und die Verwendung von C#-Komponenten in klassischen COM-Anwendungen. Obwohl diese F?higkeit strenggenommen eigentlich keine Funktion von C# ist, zeugt sie doch von einer ?hnlich pragmatischen Sicht der Dinge, wobei neue Funktionalit?t so lange wie n0tig friedlich mit Legacy-Code koexistieren kann.
Das .NET Framework Das .NET Framework von Microsoft besteht aus zwei Elementen: einer Laufzeitumgebung namens Common Language Runtime (CLR) und einer Klassenbibliothek namens Framework Class Library (FCL). Die FCL baut auf der CLR auf und stellt Dienste zur Verfgung, die von modernen Anwendungen ben0tigt werden. W?hrend Anwendungen, die auf das .NET Framework abzielen, direkt mit der FCL interagieren, fungiert die CLR als die zugrundeliegende Engine. Um das .NET Framework zu verstehen, muß man zuerst die Rolle der CLR begriffen haben.
Die Common Language Runtime Die CLR ist eine moderne Laufzeitumgebung, die die Ausfhrung von Benutzercode verwaltet und Dienste wie JIT-Kompilierung, Speichermanagement, Exception-Management, Debugging- und Profiling-Untersttzung sowie ein integriertes Sicherheits- und Berechtigungsmanagement zur Verfgung stellt. Die CLR soll fr die n?chsten zehn Jahre die Grundlage der Microsoft-Plattform sein. Doch auch ihre Entwicklung hat lange Zeit gedauert. Ihre Ursprnge reichen bis in das Jahr 1997 zurck, als Produkte wie der Microsoft Transaction Server (MTS) begannen, das Versprechen eines deklarativeren, dienstorientierteren Programmiermodells einzul0sen. In diesem neuen Modell konnten die Entwickler ihre Komponenten zur Entwicklungszeit mit deklarativen Hinweisen versehen und sich dann darauf verlassen, daß die Dienste einer Laufzeitumgebung (z.B. MTS) die Komponentenaktivierung kapern und Methodenaufrufe abfangen wrden, wobei transparent zus?tzliche Dienste wie etwa Transaktionen, Sicherheit, Just-in-Time (JIT)Aktivierung und anderes mehr als Zwischenebene eingezogen wurden. Dieses Erfordernis,
Das .NET Framework
7
Einfhrung in C#
Viele Entwurfsentscheidungen bei C# knden von einer pragmatischen Sicht der Dinge. So wurde z.B. die Syntax so gew?hlt, daß sie C-, C++ und Java-Entwicklern vertraut ist, damit C# leichter zu erlernen und die Portierung von Quellcode einfacher wird.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
immer mehr COM-Typinformationen zu bekommen, stieß an die Grenzen dessen, was mit IDL und Typbibliotheken m0glich und wnschenswert war. So trat das COM+-Team an, um eine umfassende L0sung fr dieses Problem zu suchen. Auf der 1997er PDC in San Diego wurde erstmals ein L0sungsansatz 0ffentlich diskutiert, als Mary Kirtland und andere Mitglieder des COM+-Teams eine zuknftige Version des COM vorstelleten, das sich auf ein Ding namens COM+ Runtime sttzen und viele Dienste liefern sollte, die sp?ter in der CLR wieder auftauchten, darunter erweiterbare Typinformationen, Integration in alle Sprachen, Implementierungsvererbung und automatisches Speichermanagement.1 Kurz nach der PDC von 1997 h0rte Microsoft auf, sich 0ffentlich ber diese Technologie zu ?ußern, und das Produkt namens COM+, das mit Windows 2000 herauskam, hatte nur wenig Ehnlichkeit mit der COM+ Runtime, wie sie ursprnglich beschrieben worden war. Doch hinter den Kulissen ging die Arbeit weiter, und der Umfang des Projekts wuchs in dem Maße, wie seine Bedeutung fr Microsoft zunahm. Anfangs hatte das Projekt den Codenamen Lightning, machte aber in der Folgezeit viele interne (und auch einige externe) Namenswechsel durch. Es hieß zwischenzeitlich COM3, COM+ 2.0, COM+ Runtime, NGWS Runtime, Universal Runtime (URT) und schließlich Common Language Runtime. Diese Bezeichnung tauchte letztlich als das .NET Framework wieder auf, das in Orlando, Florida, auf der PDC von Juli 2000 angekndigt wurde. In den darauffolgenden 18 Monaten machte das .NET Framework eine lange Betaphase durch, die schließlich in der Ver0ffentlichung der Version 1.0 des Microsoft .NET Framework am 15. Januar 2002 gipfelte.
Kompilierungs- und Ausfhrungsmodell Um die CLR besser verstehen zu k0nnen, mssen Sie wissen, wie sich Compiler fr das .NET Framework von den traditionellen Compilern unterscheiden. Traditionelle Compiler zielen auf einen speziellen Prozessor ab, verarbeiten Quellcodedateien in einer bestimmten Sprache und produzieren Bin?rdateien mit Anweisungsfolgen in der nativen Sprache des Zielprozessors. Diese Bin?rdateien k0nnen dann direkt auf dem Zielprozessor ausgefhrt werden. .NET-Compiler funktionieren etwas anders, denn sie zielen nicht auf einen konkreten nativen Prozessor ab, sondern verarbeiten Quellcodedateien und produzieren Bin?rdateien als zwischengeschaltete Darstellungsebene der Programmierkonstrukte, die als eine Kombination von Metadaten und der Common Intermediate Language (CIL) ausgedrckt werden. Damit diese Bin?rdateien ausgefhrt werden k0nnen, muß die CLR auf dem Zielcomputer laufen. Wenn diese Bin?rdateien ausgefhrt werden, wird die CLR geladen. Sie bernimmt dann die Steuerung und verwaltet die Ausfhrung. Sie liefert dabei verschiedene Dienste wie z.B. JITKompilierung (wobei die CIL nach Bedarf in den korrekten Anweisungsfolgen fr den zugrundeliegenden Prozessor konvertiert wird), das Speichermanagement (in Form eines Garbage Collectors), das Exception-Management, die Integration von Debugger und Profiler sowie Sicherheitsdienste (Stack-Walking und Prfung von Berechtigungen). 1 Zwei Websites enthalten Artikel von Mary Kirtland ber die COM+-Runtime: http://www.microsoft.com/ msj/defaulttop.asp?page=/msj/1197/inthisissuefeatures1197.htm und http://www.microsoft.com/msj/ defaulttop.asp?page=/msj/1297/inthisissuefeatures1297.htm.
8
Kapitel 1: Einf hrung in C# und das .NET Framework
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Diese Abh?ngigkeit von einer Laufzeitumgebung mag anfangs als Nachteil erscheinen, aber in Wirklichkeit bietet diese Architektur massive Vorteile. Da die Metadaten und die CIL-Darstellungen in bezug auf den Prozessor neutral sind, k0nnen Sie die Bin?rdateien unabh?ngig von der jeweiligen Prozessorarchitektur auf jedem Computer einsetzen, auf dem die Common Language Runtime pr?sent ist. Da die Erzeugung prozessorspezifischen Codes bis zur Laufzeit aufgeschoben wird, hat die CLR außerdem die M0glichkeit, je nach der Zielarchitektur, auf der der Code l?uft, prozessorspezifische Optimierungen vorzunehmen. Wenn die Prozessortechnologie Fortschritte macht, ben0tigen die Anwendungen lediglich eine aktualisierte Version der CLR, um von diesen Fortschritten zu profitieren. Im Gegensatz zu den traditionellen Bin?rdarstellungen, die haupts?chlich eine Reihe von nativen Prozessoranweisungen sind, bewahrt die Kombination von Metadaten und CIL fast alle Konstrukte der Quellsprache. Außerdem ist diese Darstellung quellsprachenneutral, so daß die Entwickler Anwendungen in mehreren Quellsprachen schreiben k0nnen. Sie k0nnen fr jede Aufgabe die am besten geeignete Sprache ausw?hlen und sind nicht gezwungen, fr jede Anwendung eine bestimmte Quellsprache als Standard zu definieren oder sich auf Komponententechnologien wie COM oder CORBA sttzen zu mssen, um die Unterschiede zwischen den Quellsprachen zu maskieren, in denen die getrennten Komponenten einer Anwendung geschrieben wurden.
Das Common Type System Letztlich ist die CLR dazu da, unabh?ngig von der Quellsprache den managed Code in sicherer Form zu verwalten. Um Arbeit die mit verschiedenen Programmiersprachen zu erm0glichen, die Typsicherheit zu gew?hrleisten und verwaltete Ausfhrungsdienste wie die JIT-Kompilierung, die Garbage Collection, das Exception-Management usw. zur Verfgung zu stellen, muß die CLR den managed Code, den sie gerade ausfhrt, genau kennen. Damit dies m0glich ist, definiert die CLR ein gemeinsam genutztes Typensystem namens Common Type System (CTS). Das CTS definiert, nach welchen Regeln s?mtliche Typen deklariert, definiert und verwaltet werden, egal aus welcher Quellsprache sie stammen. Das CTS wurde so reichhaltig und flexibel gestaltet, daß es eine Vielzahl von Quellsprachen untersttzt und die Grundlage fr die Arbeit mit vielen verschiedenen Programmiersprachen, Typsicherheit und verwaltenden Ausfhrungsdiensten darstellt. Compiler fr verwaltete Programmiersprachen, die vollwertige Mitglieder der CLR-Welt sein sollen, sind dafr zust?ndig, die Konstrukte der Quellsprache ihren jeweiligen CTS-Entsprechungen zuzuordnen. Wenn es keine unmittelbare Entsprechung gibt, k0nnen die Sprachdesigner beschließen, entweder die Quellsprache besser an die CTS anzupassen (um eine nahtlose Integration der Sprachen zu erm0glichen) oder noch etwas Klempnerarbeit zu leisten, um die Originalsemantik der Quellsprache zu erhalten (was jedoch zu Lasten der M0glichkeit zur Arbeit mit verschiedenen Programmiersprachen gehen kann).
Das .NET Framework
9
Einfhrung in C#
Dieses Kompilierungs- und Ausfhrungsmodell erkl?rt auch, warum C# als eine verwaltete Programmiersprache (managed language) bezeichnet wird, warum der Code, der in der CLR l?uft, managed Code heißt und warum die Ausfhrung in der CLR verwaltete Ausf hrung (managed execution) genannt wird.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Da alle Typen letztlich als CTS-Typen dargestellt werden, wird es nun m0glich, interessante neue Kombinationen von Typen zu schaffen, die in verschiedenen Sprachen geschrieben wurden. Da verwaltete Programmiersprachen letztlich CTS-Typen deklarieren und da das CTS die Vererbung untersttzt, ergibt sich, daß die CLR auch die Vererbung ber einzelne Sprachen hinweg untersttzt.
Die Common Language Specification Daß nicht alle Sprachen genau dieselben Konstrukte untersttzen, kann ein Hindernis fr die Integration verschiedener unterschiedlicher Programmiersprachen sein. Nehmen Sie folgendes Beispiel: Sprache A gestattet nicht-vorzeichenbehaftete Typen (die auch vom CTS untersttzt werden), aber Sprache B tut dies nicht. Wie soll Code, der in Sprache B geschrieben wurde, eine Methode aufrufen, die in Sprache A geschrieben wurde, wenn diese Methode einen nicht-vorzeichenbehafteten Integer als Parameter entgegennimmt? Die L0sung heißt Common Language Specification (CLS). Die CLS definiert eine vernnftige Teilmenge des CTS, die ausreichen sollte, um eine Integration der verschiedenen einzelnen Programmiersprachen zu erm0glichen, und konkrete Probleme wie z.B. nicht-vorzeichenbehaftete Integer, berladene Operatoren und dergleichen ausschließt. Jede verwaltete Programmiersprache entscheidet selbst, wieviel vom CTS sie untersttzen will. Sprachen, die jeden CLS-konformen Typ verarbeiten k0nnen, nennt man CLS-Consumer, und Sprachen, die jeden vorhandenen CLS-konformen Typ erweitern k0nnen, nennt man CLSExtender. Natrlich k0nnen verwaltete Programmiersprachen nach Belieben CTS-Features auch ber die CLS hinaus untersttzen, und die meisten tun dies auch. So ist z.B. C# sowohl CLS-Consumer als auch CLS-Extender und untersttzt alle wichtigen Features des CTS. Diese Kombination aus dem reichhaltigen und flexiblen CTS und der berall untersttzten CLS fhrte dazu, daß viele Sprachen fr die .NET-Plattform angepaßt wurden. Als dieses Buch geschrieben wurde, bot Microsoft bereits Compiler fr sechs verwaltete Programmiersprachen an (C#, VB .NET, JScript, Managed Extensions for C++, Microsoft IL und J#), und eine Vielzahl von Herstellern aus der Wirtschaft und aus den Universit?ten vertrieb verwaltete Versionen von Programmiersprachen, unter anderem fr COBOL, Eiffel, Haskell, Mercury, Mondrian, Oberon, Forth, Scheme, Smalltalk, APL, diverse Pascal-Flavors und andere mehr. Wenn man betrachtet, wieviel Aufmerksamkeit .NET von Wirtschaft und Forschung entgegengebracht wird, so kann man sagen, daß .NET so etwas wie eine Renaissance in der Entwicklung von Programmiersprachen ausgel0st hat.
Die Framework Class Library Die Bedrfnisse der Entwickler (und die F?higkeiten von Windows) haben sich stark entwikkelt, seit im November 1985 Windows 1.0 eingefhrt wurde. W?hrend Windows wuchs, um neue Bedrfnisse der Kunden zu befriedigen, wuchsen auch die APIs mit der Zeit um Gr0ßenordnungen, wurden immer komplexer und inkonsistenter und waren schließlich in ihrer Gesamtheit kaum noch zu erfassen. Hinzu kam, daß zwar moderne Paradigmen wie die Objektorientierung, Komponentensoftware und Internet-Standards entstanden und in vielen F?llen zum Mainstream wurden, aber in das Windows-Programmiermodell nicht umfassend und konsistent integriert wurden.
10
Kapitel 1: Einf hrung in C# und das .NET Framework
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Die FCL stellt eine breite Palette von Softwarediensten auf hoher Ebene zur Verfgung, die auf die Bedrfnisse moderner Anwendungen zugeschnitten sind. Konzeptionell k0nnen diese in sieben Kategorien eingeteilt werden:
· · · · ·
Untersttzung fr die Kernfunktionalit?t, z.B. die Interaktion mit Grunddatentypen und Collections, Konsolen-, Netzwerk- und Datei-I/O und fr die Interaktion mit anderen Elementen der Laufzeitumgebung Untersttzung fr die Interaktion mit Datenbanken; die Verarbeitung und Erzeugung von XML und die Verarbeitung von Daten in Tabellen- und Baumstrukturen Untersttzung fr Webanwendungen (Thin-Client-Anwendungen) mit einem umfangreichen serverseitigen Event-Modell Untersttzung fr die Erstellung von Desktop-Anwendungen (Thick-Client-Anwendungen) einschließlich einer umfassenden Untersttzung fr die Windows-Benutzeroberfl?che Untersttzung fr die Erstellung SOAP-basierter XML-Web Services
Die FCL ist eine gewaltige Bibliothek mit mehr als 3.500 Klassen. In Kapitel 5 finden Sie einen detaillierteren Fberblick ber den Inhalt der FCL.
ECMA-Standardisierung Sehr ermutigend an dem .NET Framework ist der Grad an Offenheit, den Microsoft w?hrend der Entwicklung dieser Plattform an den Tag legte. Von den ersten 0ffentlichen Vorpremieren an waren die Kernspezifikationen zu C#, die Klassen der FCL und die inneren Vorg?nge in der CLR frei zug?nglich. Diese Offenheit nahm jedoch ganz neue Dimensionen an, als Microsoft im November 2000 mit seinen Co-Sponsoren Intel und HP offiziell die Spezifikationen der Programmiersprache C#, einen Teil der FCL und die Laufzeitumgebung der ECMA zur Standardisierung vorlegte. Damit begann ein intensiver Standardisierungsprozeß, an dem Microsoft, HP, Intel, IBM, Fujitsu, Sun, Netscape, Plum Hall, OpenWave und andere Organisationen beteiligt waren. Die Arbeit fand unter der Egide des ECMA Technical Committee TC39 statt, das zuvor die Programmiersprache JavaScript als ECMAScript standardisiert hatte. TC39 heuerte zwei neue Arbeitsgruppen fr die eigentliche Standardisierungsarbeit an: Die eine konzentrierte sich auf C# und die andere auf das, was sp?ter die Common Language Infrastructure (CLI) wurde. Die CLI bestand aus der Laufzeit-Engine und dem Teil der FCL, der standardisiert wurde. Vom Konzept her soll die CLR von Microsoft eine standardkonforme, kommerzielle Implementierung der Laufzeit-Engine sein, die in der CLI spezifiziert wird; und die FCL von Microsoft soll
ECMA-Standardisierung
11
Einfhrung in C#
Wenn Sie diese Probleme sehen und bercksichtigen, wie viele Enderungen allein schon durch den Fbergang zu einer verwaltenden Ausfhrungsumgebung notwendig werden, dann ist klar, daß die Zeit fr einen sauberen Neubeginn gekommen war. Infolgedessen ersetzt das .NET Framework die meisten (wenn auch nicht alle) traditionellen Windows-APIs durch eine gut ausgestattete, objektorientierte Klassenbibliothek namens Framework Class Library (FCL).
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
eine standardkonforme, kommerzielle Implementierung der Klassenbibliothek sein, die in der CLI spezifiziert wird (obwohl sie natrlich viel mehr ist als nur die 294 Klassen, die letztlich in der CLI spezifiziert wurden). Nach mehr als einem Jahr harter Arbeit hatten die Arbeitsgruppen ihre Standardisierungsbemhungen abgeschlossen und legten die Spezifikationen der ECMA General Assembly vor. Am 13. Dezember 2001 genehmigte diese General Assembly die Spezifikationen von C# und CLI als internationalen Standard und gab ihnen die Nummern ECMA-334 (C#) ECMA-335 (CLI). Kopien der ECMA-Standards erhalten Sie unter http://www.ecma.ch. Kritiker haben behauptet, die ECMA-Standardisierung sei nichts als ein Trick, mit dem Microsoft von den plattformbergreifenden Vorteilen von Java ablenken wollte. Doch die Fachleute, die diese Standardisierung durchfhrten, waren so hochqualifiziert und so eng in den langwierigen Standardisierungsprozeß eingebunden, daß diese Kritik ins Leere geht. Microsoft sowie seine Co-Sponsoren und die anderen Mitglieder der Standardisierungsteams betrauten einige ihrer besten und klgsten K0pfe mit dieser Aufgabe und investierten viel Zeit und Mhe in die Standardisierung. Angesichts der Tatsache, daß diese Arbeit parallel zur Entwicklung und Ver0ffentlichung des .NET Framework lief, sprechen diese gewaltigen Investitionen von Microsoft und anderen den Verschw0rungstheorien hohn. Damit ein Standard auch Wirkung zeigen kann, muß er natrich implementiert werden. Zu dem Zeitpunkt, als dies geschrieben wurde, waren noch nicht viele standardkonformen Referenzimplementierungen ausgeliefert worden. Doch die Arbeit an Open- und Shared SourceImplementierungen fr C# und die CLI, sowohl fr kommerzielle als auch fr nichtkommerzielle Zwecke, ist bereits fortgeschritten. Microsoft arbeitet zusammem mit Corel an einer Shared Source-Implementierung der CLI und an darauf abgestimmten Compilern fr C# und ECMAScript. Dieses Projekt, das den Codenamen Rotor tr?gt, wurde am 27. M?rz 2002 freigegeben und l?uft sowohl unter Windows als auch unter FreeBSD. Mit der Shared Source-Lizenz wird diese CLI-Implementierung fr den nichtkommerziellen Einsatz in Forschung und Lehre und fr Hobbyprogrammierer lizensiert. Doch die Implementierung von Microsoft ist nicht die einzige. Auch Mono und dotGNU arbeiten an CLI-Implementierungen. Das Mono-Projekt (http://www.go-mono.com), das von der Ximian Corporation begonnen wurde, soll vollst?ndige Open Source-Implementierungen (GPL und LGPL) liefern, und zwar nicht nur fr die CLI-Plattform und den C#-Compiler, sondern auch fr eine gr0ßere Anzahl Klassen aus der FCL des .NET Framework von Microsoft. Zus?tzlich zu den internen Ressourcen, die Ximian fr das Projekt zur Verfgung stellte, zog das Mono-Projekt auch die Aufmerksamkeit der Open Source-Gemeinschaft auf sich und gewinnt nun massiv an Fahrt. Abschließend sei noch auf das Open Source-Projekt (GPL) dotGNU hingewiesen (http://www. dotgnu.org). Dieses ist zwar nicht so bekannt wie Mono, kommt aber dennoch voran, nicht zuletzt wegen einiger interessanter und einzigartiger Konzepte, die es einfhrt. Der Kern von dotGNU heißt Portable.NET und war ursprnglich das Kind eines einzelnen Entwicklers (Rhys Weatherley), bevor er im August 2000 sein Projekt in dotGNU einbrachte. Das Besondere am dotGNU-Projekt ist, daß es ursprnglich um einen CLI-Interpreter und nicht um einen JITCompiler herum entworfen wurde (obwohl das Team plant, zu einem sp?teren Zeitpunkt auch eine JIT-Kompilierung hinzuzufgen), und die Entwickler planen, die direkte Ausfhrung von Java-Binaries zu untersttzen.
12
Kapitel 1: Einf hrung in C# und das .NET Framework
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
ECMA-Standardisierung
13
Einfhrung in C#
Sehr wahrscheinlich werden mit der Zeit noch mehr CLI-Implementierungen entstehen. Es ist zwar noch zu frh, um zu sagen, ob das .NET Framework (in Form der CLI) jemals auf so vielen Plattformen zur Verfgung stehen wird wie heute Java, aber das bisherige Maß an Aufgeschlossenheit und Interesse seitens der Programmierergemeinschaft l?ßt das Beste hoffen.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
KAPITEL 2
Grundlagen von C# In diesem Kapitel wird ein einfaches C#-Programm erkl?rt, und die Grundlagen von C# werden eingefhrt.
Das erste C#-Programm Im Folgenden sehen Sie ein einfaches C#-Programm: namespace FirstProgram { using System; class Example { static void Main () { Console.WriteLine ("Hello world!"); } } }
Ein C#-Programm besteht aus Typen (in der Regel sind dies Klassen), die in Namensr?umen organisiert sind. Jeder Typ enth?lt Funktions-Member (Methoden und Eigenschaften) sowie Daten-Member (Felder). Methoden enthalten eine Reihe von Anweisungen, die nacheinander ausgefhrt werden. In unserem Programm definieren wir eine Klasse namens Example mit einer Methode namens Main, deren einzige Anweisung die Worte Hello world! in das Konsolenfenster schreibt. C# erkennt diese Methode als Standardeinstiegspunkt fr die Ausfhrung, also als den Punkt, an dem das Programm anf?ngt. Die Klasse Console kapselt eine Standard-Ein-Ausgabefunktionalit?t und stellt Methoden wie z.B. WriteLine zur Verfgung. Um Typen aus einem anderen Namensraum zu benutzen, verwenden Sie die using-Anweisung. Da sich die Klasse Console im Namensraum System befindet, gehen wir ber System. Typen aus anderen Namensr?umen k0nnten entsprechend unsere Klasse Example nutzen, indem sie auf FirstProgram zugreifen. In C# gibt es keine selbst?ndigen Funktionen. Funktionen h?ngen immer mit einem Typ oder, wie Sie noch sehen werden, mit Instanzen eines Typs zusammen. Unser Programm ist ganz
14
Kapitel 2: Grundlagen von C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
einfach und nutzt nur statische Member. Das bedeutet, daß das Member mit seinem Typ verbunden ist, und nicht mit Instanzen seines Typs. Außerdem verwenden wir nur void-Methoden, also Methoden, die keinen Wert zurckgeben. Im weiteren Verlauf dieses Buchs enthalten die meisten Beispiele den folgenden Coderumpf: Grundlagen von C#
using System; class Test { static void Main () { ... } }
Bezeichner und Schlsselw-rter Bezeichner sind die Namen, die Programmierer ihren Typen, Methoden, Variablen usw. geben. Ein Bezeichner muß ein ganzes Wort sein und besteht im wesentlichen aus UnicodeZeichen, die mit einem Buchstaben oder einem Unterstrich beginnen. Sie drfen nicht mit einem Schlsselwort in Konflikt treten. Als Sonderfall wird mit dem Pr?fix @ ein Namenskonflikt mit einem Schlsselwort verhindert, aber dieses Zeichen wird nicht als Bestandteil des Bezeichners betrachtet. Die folgenden beiden Bezeichner sind somit ?quivalent: KoRn @KoRn
C#-Bezeichner unterscheiden zwar zwischen Groß- und Kleinschreibung, aber aus Grnden der Kompatibilit?t mit anderen Sprachen sollten Sie darauf achten, daß sich 0ffentliche oder geschtzte Bezeichner nicht durch die Groß- und Kleinschreibung allein unterscheiden. In C# gibt es folgende Schlsselw0rter: abstract
as
base
bool
break
byte
case
catch
char
checked
class
const
continue
decimal
default
delegate
do
double
else
enum
event
explicit
extern
false
finally
fixed
float
for
foreach
goto
if
implicit
in
int
interface
internal
is
lock
long
namespace
new
null
object
operator
out
override
params
private
protected
public
readonly
ref
return
sbyte
sealed
short
sizeof
stackalloc
static
string
Bezeichner und Schl sselw@rter
15
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
struct
switch
this
throw
true
try
typeof
uint
ulong
unchecked
unsafe
ushort
using
virtual
void
while
Grundlegendes zu den Typen Ein C#-Programm verstehen Sie am besten anhand seiner drei Grundelemente: Funktionen, Daten und Typen. Da dieses Buch bereits einige Programmiererfahrung voraussetzt, geben wir als erstes einen Fberblick ber die Funktionen und Daten (diese drften Sie bereits kennen) und erkl?ren dann die Typen etwas genauer. Funktionen Eine Funktion fhrt eine Aktion durch, indem sie eine Reihe von Anweisungen ausfhrt. So k0nnen Sie z.B. eine Funktion haben, die den Abstand zwischen zwei Punkten zurckgibt, oder eine Funktion, die den Durchschnitt aus einem Array von Werten berechnet. Mit einer Funktion k0nnen Daten verarbeitet werden. Daten Daten sind die Werte, auf denen Funktionen operieren. Dies k0nnen z.B. die Koordinaten eines Punktes oder ein Array von Werten sein. Daten haben immer einen bestimmten Typ. Typen Ein Typ hat Daten-Member und Funktions-Member. Die Funktions-Member dienen dazu, die Daten-Member zu verarbeiten. Die h?ufigsten Typen sind Klassen und Structs, denn sie liefern eine Schablone, um Daten zu erzeugen. Daten sind immer Instanzen von Typen. Da Typen ein ziemlich abstraktes Konzept sind, betrachten wir als erstes zwei konkrete Beispiele.
Die Klasse String Die Klasse String spezifiziert eine Zeichenfolge. Das bedeutet: Sie k0nnen Werte wie ».NET« oder »http://oreilly.com« als Strings speichern. Sie k0nnen aber auch Funktionen auf Strings ausfhren, beispielsweise das Zeichen an einer bestimmten String-Position zurckgeben oder die Darstellung eines Strings in Kleinschreibung erhalten. In diesem Beispiel geben wir den String ».NET« in Kleinbuchstaben aus (».net«) und rufen die L?nge des Strings ab (4). Dazu erzeugen wir als erstes eine Instanz eines Strings und verwenden dann die Methode ToLower und die Eigenschaft Length. Beide sind Funktions-Member dieses Strings. using System; class Test { static void Main () { string s = ".NET"; Console.WriteLine (s.ToLower()); // Ausgabe: ".net"
16
Kapitel 2: Grundlagen von C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Console.WriteLine (s.Length); // Ausgabe: 4 } }
Das Struct int
In diesem Beispiel geben wir das Ergebnis einer Multiplikation von 3 mal 4 aus. Dazu erzeugen wir zwei int-Instanzen und verwenden dann den Operator *. Der Typ int ist ein eingebauter Typ, der eine elementare Rechenfunktionalit?t (z.B. *) untersttzt. Es ist aber hilfreicher, sich den Operator * als Funktions-Member des Typs int vorzustellen. using System; class Example { static void Main () { int a = 3; int b = 4; Console.WriteLine (a * b); } }
Ein benutzerdefinierter Typ In C# k0nnen Sie auch eigene Typen definieren. Eigentlich ist es ja das Wesen der Programmierung, neue Typen zu definieren, die jeweils eine Reihe von Daten- und Funktions-Membern haben. In diesem Beispiel erstellen wir einen neuen Typ namens Counter: // Importiert Typen aus dem Namensraum System, z.B. Console using System; class Counter { // Neue Typen sind in der Regel Klassen oder Structs // --- Daten-Member --int value; // Feld vom Typ int int scaleFactor; // Feld vom Typ int // Konstruktor zur Initialisierung einer Instanz des Typs public Counter(int scaleFactor) { this.scaleFactor = scaleFactor; } // Methode public void Inc() { value+=scaleFactor; } // Eigenschaft public int Count { get {return value; } } }
Grundlegendes zu den Typen
17
Grundlagen von C#
Das Struct int spezifiziert einen vorzeichenbehafteten Integer (eine positive oder negative ganze Zahl) mit einer L?nge von 32 Bit. Das bedeutet, daß Sie in diesem Typ Werte zwischen -2147483648 und 2147483647 (oder zwischen -2n-1 und 2n-1-1, wobei n=32) speichern k0nnen. Auf einem int k0nnen Sie Operationen wie die Addition, Multiplikation usw. ausfhren.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
class Test { // Ausf=hrung beginnt hier static void Main() { // Erzeuge eine Instanz vom Typ Counter Counter c = new Counter(5); c.Inc(); c.Inc(); Console.WriteLine(c.Count); // Ausgabe: "10"; // Erzeuge noch eine Instanz vom Typ Counter Counter d = new Counter(7); d.Inc(); Console.WriteLine(d.Count); // Ausgabe:"7"; } }
Instanzen von Typen Um einen Typ zu benutzen, mssen Sie eine Instanz davon erzeugen. Daten- und FunktionsMember, die Sie nur benutzen k0nnen, wenn Sie vorher einen entsprechenden Typ instantiiert haben, nennt man Instanz-Member (und standardm?ßig sind Member immer Instanz-Member). Daten- und Funktions-Member, die auf dem Typ selbst verwendet werden k0nnen, heißen statische Member. In diesem Beispiel gibt die Instanzmethode PrintName den Namen eines bestimmten Pandab?ren aus, w?hrend die statische Methode PrintSpeciesName den Namen ausgibt, der allen Pandab?ren in der Anwendung (AppDomain) gemeinsam ist: using System; class Panda { string name; static string speciesName = "Ailuropoda melanoleuca"; // Initialisiert Panda (Siehe Instanzkonstruktoren) public Panda(string n) { name = n; } public void PrintName() { Console.WriteLine(name); } public static void PrintSpeciesName() { Console.WriteLine(speciesName); } } class Test { static void Main() { Panda.PrintSpeciesName(); // Rufe statische Methode auf Panda p1 = new Panda("Petey"); Panda p2 = new Panda("Jimmy"); p1.PrintName(); // Rufe Instanzmethode auf
18
Kapitel 2: Grundlagen von C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
p2.PrintName(); // Rufe Instanzmethode auf } }
Typ-Konvertierung Jeder Typ hat eigene Regeln, die definieren, wie er in andere Typen und aus anderen Typen konvertiert werden kann. Konvertierungen k0nnen implizit und explizit ablaufen. Implizite Konvertierungen werden automatisch durchgefhrt, explizite hingegen erfordern eine Typumwandlung: int x = 123456; // int ist ein 4-Byte-Integer long y = x; // implizite Konvertierung in einen 8-Byte-Integer short z =(short)x // explizite Konvertierung in einen 2-Byte-Integer
Implizite Konvertierungen sind m0glich, wenn sie garantiert erfolgreich sind und ohne Datenverlust verlaufen. Im Umkehrschluß ist eine explizite Konvertierung erforderlich, wenn entweder der Erfolg oder der Mißerfolg der Konvertierung von den Umst?nden zur Laufzeit abh?ngt oder wenn bei der Konvertierung Informationen verlorengehen k0nnen. Die meisten Konvertierungsregeln werden von der Sprache selbst definiert, z.B. die Regeln fr die oben gezeigten numerischen Konvertierungen. Gelegentlich kann es Programmierern ntzen, eigene implizite und explizite Konvertierungen zu definieren, wie es weiter unten in diesem Kapitel beschrieben wird.
Werttypen und Referenztypen Alle C#-Typen lassen sich in die folgenden Kategorien einordnen:
· · ·
Werttypen (Struct, Enum) Referenztypen (Klasse, Array, Delegate, Interface) Zeigertypen
Der wesentliche Unterschied zwischen diesen drei Hauptkategorien (Werttypen, Referenztypen und Zeigertypen) ist die Art, wie im Speicher damit umgegangen wird. Die folgenden Abschnitte beschreiben die wichtigsten Unterschiede zwischen Werttypen und Referenztypen. Da Zeigertypen in C# eher ungew0hnlich sind, werden sie sp?ter in Kapitel 4 erkl?rt.
Werttypen und Referenztypen
19
Grundlagen von C#
Beachten Sie, daß Sie den Typnamen angeben mssen, um statische Member außerhalb ihres bergeordneten Typs aufrufen zu k0nnen. Vielleicht sind Sie schon selbst darauf gekommen, daß die WriteLine-Methode von Console ein statisches Member ist. Es ist mit der Klasse Console selbst verbunden, und nicht mit einer Instanz dieser Klasse. Ein Funktions-Member sollte statisch sein, wenn es nicht von irgendwelchen Daten-Membern einer Instanz abh?ngig ist. So ist auch die Methode Main in allen Programmen ein statisches Member, was bedeutet, daß Sie diese Methode aufrufen k0nnen, ohne eine Instanz ihrer bergeordneten Klasse erzeugen zu mssen.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Werttypen Die Werttypen sind am einfachsten zu verstehen. Sie enthalten ihre Daten unmittelbar. Dies gilt z.B. fr den Typ int (er speichert einen Integer) oder den Typ bool (er speichert den Wert wahr oder falsch). Hauptmerkmal eines Werttyps ist: Wenn Sie einen Wert einem anderen zuweisen, legen Sie eine Kopie dieses Wertes an. Ein Beispiel: using System; class Test { static void Main () { int x = 3; int y = x; // Weise x zu y zu. y ist jetzt eine Kopie von x x++; // Inkrementiere x auf 4 Console.WriteLine (y); // Ausgabe: 3 } }
Referenztypen Referenztypen sind ein wenig komplizierter. Ein Referenztyp definiert in Wirklichkeit zwei getrennte Sachen: Ein Objekt und eine Referenz auf dieses Objekt. Das folgende Beispiel folgt genau demselben Muster wie das vorherige. Beachten Sie jedoch, wie hier die Variable y aktualisiert wird, w?hrend sie im vorherigen Beispiel unver?ndert blieb: using System; using System.Text; class Test { static void Main () { StringBuilder x = new StringBuilder ("Hallo"); StringBuilder y = x; x.Append (" miteinander"); Console.WriteLine (y); // Ausgabe: "Hallo miteinander" } }
Das liegt daran, daß der Typ StringBuilder ein Referenztyp ist, w?hrend int ein Werttyp ist. Als wir die Variable StringBuilder deklarierten, taten wir in Wirklichkeit zwei Dinge, die sich auf folgende beiden Zeilen aufspalten lassen: StringBuilder x; x = new StringBuilder ("Hallo");
Die erste Zeile erzeugt eine neue Referenz auf einen StringBuilder. Die zweite Zeile weist dieser Referenz ein neues StringBuilder-Objekt zu. Betrachten wir nun die n?chste Zeile: StringBuilder y = x;
Wenn wir y zu x zuweisen, sagen wir damit: »y soll auf dasselbe Ding zeigen wie x.« Eine Referenz speichert die Adresse eines Objekts (eine Adresse ist ein Speicherort, der als eine Zahl mit vier Bytes gespeichert wird). Eigentlich machen wir nach wie vor nur eine Kopie von x, aber nun kopieren wir nicht das StringBuilder-Objekt selbst, sondern die erw?hnte, vier Byte lange Zahl.
20
Kapitel 2: Grundlagen von C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Betrachten Sie folgende Zeile: x.Append (" miteinander");
y.Append (" miteinander");
Eine Referenz kann auch auf gar kein Objekt zeigen, n?mlich dann, wenn Sie dieser Referenz null zuweisen. In diesem Codebeispiel weisen wir x null zu, k0nnen aber immer noch auf dasselbe StringBuilder-Objekt zugreifen, das wir ber y erzeugt hatten: using System; using System.Text; class Test { static void Main () { StringBuilder x; x = new StringBuilder ("Hallo"); StringBuilder y = x; x = null; y.Append (" miteinander"); Console.WriteLine (y); // Ausgabe: "Hallo miteinander" } }
Der Heap und der Stack Der Stack ist ein Speicherblock, der bei jedem Eintritt in eine Funktion w?chst (um lokale Variablen zu speichern) und bei jedem Austritt aus einer Funktion wieder schrumpft (weil die lokalen Variablen nicht mehr ben0tigt werden). In unserem obigen Beispiel sind die Referenzen x und y nach dem Abschluß der Main-Funktion nicht mehr sichtbar. Dasselbe gilt fr alle Werttypen, die in der Funktion deklariert wurden. Der Grund: Diese Werte werden im Stack gespeichert. Der Heap ist ein Speicherblock, in dem Referenztyp-Objekte gespeichert werden. Immer wenn ein Objekt erzeugt wird, wird es dem Heap zugewiesen, und eine Referenz auf dieses Objekt wird zurckgegeben. W?hrend der Programmausfhrung fllt sich der Heap nach und nach, wenn neue Objekte erzeugt werden. Die Laufzeitumgebung verfgt ber einen Garbage Collector, der dereferenzierte Objekte aus dem Heap wieder herausnimmt, damit Ihrem Computer nicht der Speicherplatz ausgeht. Sobald festgestellt wird, daß auf ein Objekt keinerlei Referenzen mehr vorhanden sind, wird dieses aus dem Speicher entfernt. In C# k0nnen Sie Objekte nicht explizit l0schen. Entweder wird ein Objekt automatisch aus dem Stack entnommen, oder es wird automatisch von der Garbage Collection eingesammelt.
Werttypen und Referenztypen im Vergleich Die Unterschiede zwischen Wert- und Referenztypen verstehen Sie am besten, wenn Sie sie einander gegenberstellen. In C# k0nnen Sie sowohl eigene Referenz- als auch eigene Werttypen definieren. Wenn Sie einen einfachen Typ wie z.B. eine Zahl definieren m0chten, ist es
Werttypen und Referenztypen
21
Grundlagen von C#
Hier passieren eigentlich zwei Dinge: Zuerst wird die Speicherstelle gesucht, die x repr?sentiert, und dann wird das an dieser Speicherstelle befindliche StringBuilder-Objekt angewiesen, »miteinander« daran anzufgen. Genau dasselbe h?tten wir erreichen k0nnen, wenn wir »miteinander« an y angeh?ngt h?tten, denn x und y verweisen auf dasselbe Objekt:
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
angebracht, diesen als einen Werttyp anzulegen, wenn Effizienz und das Kopieren by value wnschenswert ist. Andernfalls sollten Sie einen Referenztyp definieren. Sie definieren einen neuen Werttyp, indem Sie ein Struct deklarieren, und einen neuen Referenztyp, indem Sie eine Klasse anlegen. Um eine Instanz eines Wert- oder Referenztyps zu erzeugen, rufen Sie den Konstruktor dieses Typs mit dem Schlsselwort new auf. Ein Werttyp-Konstruktor initialisiert einfach nur ein Objekt; ein Referenztyp-Konstruktor erzeugt ein neues Objekt auf dem Heap und initialisiert es dann: // Referenztyp-Deklaration class PointR { public int x, y; } // Werttyp-Deklaration struct PointV { public int x, y; } class Test { static void Main() { PointR a; // Lokale Referenztyp-Variable, belegt 4 Bytes // Speicher im Stack, um Adressen zu speichern PointV b; // Lokale Werttyp-Variable, belegt 8 Bytes // Speicher im Stack, um x und y zu speichern a = new PointR(); // Zuweisung der Referenz zur Adresse der neuen // Instanz von PointR, die im Heap angelegt // wurde. Das Objekt auf dem Heap belegt 8 // Bytes Speicher f=r x und y und weitere // 8 Bytes f=r die Kernerfordernisse eines // Objekts, z.B. um den Synchronisierungs// zustand des Objekttyps zu speichern b = new PointV(); // Ruft den Standardkonstruktor des Werttyps // auf. Der Standardkonstruktor setzt bei // PointR ebenso wie PointV jedes Feld // auf seinen Default-Wert. Dieser ist 0 // f=r x und y. a.x = 7; b.x = 7; } } // Am Ende der Methode sind zwar die lokalen Variablen a und b nicht mehr // sichtbar, aber die neue Instanz eines PointR verbleibt im Speicher, bis der // Garbage Collector merkt, daß sie nicht mehr referenziert wird
Die Zuweisung zu einem Referenztyp kopiert eine Objektreferenz, die Zuweisung zu einem Werttyp kopiert hingegen einen Objektwert: ... PointR c = a; PointV d = b; c.x = 9; d.x = 9; Console.WriteLine(a.x); // Ausgabe: 9
22
Kapitel 2: Grundlagen von C#
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Console.WriteLine(b.x); // Ausgabe: 7 } }
Einheitliches Typensystem C# stellt ein einheitliches Typensystem zur Verfgung, wodurch die Klasse Object letztlich sowohl fr Referenz- als auch fr Werttypen der Basistyp ist. Das bedeutet, daß außer den gelegentlich verwendeten Zeigertypen alle Typen dieselben Grundmerkmale haben.
Elementare Typen sind Werttypen Elementare Typen werden so genannt, weil sie sich zumeist direkt in Maschinencode darstellen lassen. So entsprechen beispielsweise die Fließkommazahlen von C# den Fließkommazahlen der meisten Prozessoren wie z.B. des Pentium. Daher haben sie in den meisten Programmiersprachen eine Sonderstellung, was allerdings dazu fhrt, daß fr elementare und benutzerdefinierte Typen unterschiedliche Regeln gelten. In C# gelten fr alle Typen dieselben Regeln, was das Programmieren vereinfacht. Um dies m0glich zu machen, sind die elementaren Typen in C# Aliase von Structs aus dem Namensraum System. So ist z.B. der Typ int ein Alias fr das Struct System.Int32, der Typ long ein Alias fr das Struct System.Int64 usw. Aus diesem Grunde haben elementare Typen dieselben Merkmale, die man auch von jedem benutzerdefinierten Typ erwarten wrde. So hat z.B. der Typ int Funktions-Member: int i = 3; string s = i.ToString();
Dies ist ?quivalent zu: // Dies ist eine erklLrende Version von System.Int32 namespace System { struct Int32 { ... public string ToString() { return ...; } } } // Das ist zwar g=ltiger Code, aber wir empfehlen den int-Alias System.Int32 i = 5; string s = i.ToString();
Werttypen und Referenztypen
23
Grundlagen von C#
Wie es in diesem Beispiel der Fall ist, k0nnen auf ein Objekt auf dem Heap auch mehrere Variablen zeigen, w?hrend ein Objekt auf dem Stack oder ein Inline-Objekt nur ber die Variable angesprochen werden kann, mit der es deklariert wurde. »Inline« bedeutet, daß die Variable Teil eines gr0ßeren Objekts ist, d.h. als Daten-Member oder Array-Member vorhanden ist.
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Werttypen erweitern die elementaren Typen Elementare Typen haben zwei ntzliche Eigenschaften: Sie sind effizient und das Kopieren by value ist intuitiv nachvollziehbar. Betrachten Sie noch einmal, wie natrlich es ist, eine Zahl einer anderen zuzuweisen und eine Kopie des Wertes dieser Zahl statt einer Kopie einer Referenz auf diese Zahl zu bekommen. In C# sind Werttypen so definiert, daß sie die Menge der elementaren Typen erweitern. In diesem Beispiel betrachten wir noch einmal unser Beispiel zu PointV und PointR, richten unser Augenmerk aber diesmal auf die Effizienz. Ein Array mit 1.000 int-Werten anzulegen ist sehr effizient. Sie weisen damit 1.000 int-Werte einem einzigen, zusammenh?ngenden Speicherblock zu: int[] a = new int[1000];
Ein Array des Werttyps PointV l?ßt sich ?hnlich effizient anlegen: struct PointV { public int x, y } PointV[] a = new PointV[1000];
H?tten wir den Referenztyp PointR verwendet, so mßten wir nach dem Instantiieren des Arrays auch noch 1.000 separate Punkte instantiieren: class PointR { public int x, y; } PointR[] a = new PointR[1000]; // Erzeugt ein Array mit 1000 Nullreferenzen for (int i=0; i]+?)[ '""]?>"; MatchCollection mc18 = Regex.Matches(t18, p18, "si");
string t19 = "Hanley A. Strappman"; string p19 = @"^\S+\s+(\S)\S*\s+\S"; Match m19 = Regex.Match(t19, p19); Strings
string t20 = @"2' 2"" "; string p20 = "\"([^\"]*)"; string r20 = Regex.Replace(t20, p20, "``$1''");
Kochbuch f&r regul8re Ausdr&cke
125
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
KAPITEL 7
Collections Collections sind Standarddatenstrukturen und erg nzen Arrays, die in C# die einzigen eingebauten Datenstrukturen sind. Darin unterscheidet sich C# von anderen Sprachen wie Perl oder Python, bei denen bereits Schl-ssel/Wert-Datenstrukturen oder Arrays dynamischer Gr)ße zum Sprachumfang geh)ren. Die FCL bietet einen Satz an Typen, die oft ben)tigte Datenstrukturen modellieren, sowie Unterst-tzung zur Erstellung eigener Datenstrukturen. Diese Typen lassen sich grob in zwei Kategorien einteilen: Interfaces, die eine Menge standardisierter Entwurfsmuster f-r Collections im allgemeinen festlegen, und konkrete Klassen, die diese Interfaces implementieren und eine breite Palette an Datenstrukturen zur Verf-gung stellen. Dieses Kapitel geht auf alle konkreten Collection-Klassen und abstrakten Collection-Interfaces ein und zeigt Beispiele f-r ihre Anwendung. Wenn nichts anderes angegeben ist, geh)ren die hier benutzten Typen den Namensr umen System.Collections oder System.Collections.Specialized an, die beide in Teil IV, dem Referenzteil dieses Buches, dokumentiert sind.
Iterieren ber Collections In Programmiersprachen existieren viele verschiedene Arten von Collections – von einfachen Datenstrukturen wie Arrays oder verketteten Listen bis hin zu komplexen wie Rot/SchwarzB umen oder Priorit ts-Queues. W hrend die interne Implementierung und nach außen sichtbaren Charakteristika sich bei all diesen Datenstrukturen mitunter deutlich voneinander unterscheiden, ist die F higkeit, die Inhalte einer Collection zu traversieren, eine fast universelle Anforderung. In der FCL wird dies durch ein Paar von Interfaces (IEnumerable und IEnumerator) unterst-tzt, die es verschiedenen Datenstrukturen erlauben, eine gemeinsame API zum Traversieren ihrer Elemente zur Verf-gung zu stellen.
126
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Die Interfaces IEnumerable und IEnumerator Um einen Enumerator nach außen zur Verf-gung zu stellen, implementiert eine Collection das Interface IEnumerable. Durch dieses Interface erh lt ein Client Zugriff auf eine Instanz des Interfaces IEnumerator. Diese Referenz ist ein logischer Zeiger, der dazu benutzt werden kann, -ber die Elemente einer Collection – allerdings nur in eine Richtung, n mlich vorw rts – zu iterieren. Die Interfaces sehen wie folgt aus: public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { bool MoveNext(); object Current {get;} void Reset(); }
Das Interface IEnumerable hat lediglich eine Methode: GetEnumerator(). Diese Methode gibt eine Referenz auf das Interface IEnumerator zur-ck, wobei der Enumerator vor dem ersten Element der Collection steht. Das Interface IEnumerator bietet Standardmechanismen (die Methoden MoveNext() und Reset()) zum Iterieren -ber die Collection und zum Zugreifen auf das jeweils aktuelle Element (die Eigenschaft Current) als eine allgemeine object-Referenz. Die Collection kann dann auf eine der beiden folgenden Weisen durchlaufen werden: MyCollection myc = new MyCollection(); ... // Benutzung von IEnumerator: Ersetzen Sie XXX durch den tatsYchlichen Typnamen IEnumerator ie = myc.GetEnumerator(); while (myc.MoveNext()) { XXX item = (XXX)myc.Current; Console.WriteLine(item); ... }
MyCollection mycoll = new MyCollection ... // Benutzung von foreach: Ersetze XXX durch den tatsYchlichen Typnamen foreach (XXX item in mcoll) { Console.WriteLine(item); ... }
Implementieren von IEnumerable und IEnumerator Damit eigene Datenstrukturen -ber den Standardmechanismus durchlaufen werden k)nnen (achten Sie bei der Implementierung von IEnumerable und IEnumerator darauf, die semanti-
Iterieren &ber Collections
127
Collections
C# bietet auch die Anweisung foreach, die auf jeder Collection arbeiten kann, die entweder das Interface IEnumerable implementiert oder eine zug ngliche GetEnumerator()-Methode zur Verf-gung stellt, die einen Typ mit zug nglichen MoveNext()- und Reset()-Methoden zur-ckgibt. Die Benutzung von foreach kann Code zur Iteration vereinfachen, wie man am n chsten Beispiel erkennen kann:
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
schen Kontrakte der beiden Interfaces einzuhalten), wird IEnumerator oft als innere Hilfsklasse benutzt, die initialisiert wird, indem die Collection dem Konstruktor des IEnumerator -bergeben wird: using System.Collections; public class MyCollection : IEnumerable { // ... int[] data; public virtual IEnumerator GetEnumerator () { return new MyCollection.Enumerator(this); } private class Enumerator : IEnumerator { MyCollection outer; int currentIndex = -1; internal Enumerator(MyCollection outer) { this.outer = outer; } public object Current { get { if (currentIndex == outer.data.Length) throw new InvalidOperationException(); return outer.data[currentIndex]; // Boxing! } } public bool MoveNext() { if (currentIndex > outer.data.Length) throw new InvalidOperationException(); return ++currentIndex < outer.data.Length; } public void Reset() { currentIndex = -1; } } }
Foreach optimieren Einer der Nachteile der Implementierung von IEnumerable und IEnumerator, wie sie im vorhergehenden Beispiel gezeigt wurde, ist der R-ckgabetyp von IEnumerator.Current: Er ist object. Dieser Ansatz hat zwei wesentliche Nachteile: 1.
Clients m-ssen die object-Referenz zu einem spezifischeren Typ umwandeln. Das ist einerseits ineffizient, vergleicht man es mit der strenger typisierten Schnittstelle homogener Collections, und andererseits eine m)gliche Ursache f-r TypeCastExceptions sein kann, wenn die Umwandlung fehlschl gt.
2.
Wenn eine Collection Werttypen enth lt, f-hrt eine R-ckgabe als Objektreferenzen dazu, daß sie zuerst mit Boxing verpackt und anschließend wieder entpackt werden m-ssen. Das ist ineffizient, erzeugt unn)tigen Garbage auf dem Heap und macht es unm)glich, den Inhalt der Collection direkt zu bearbeiten.
128
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Gl-cklicherweise gibt es einige M)glichkeiten, um diese Probleme zu l)sen. Obwohl die Anweisung foreach daf-r entworfen wurde, mit Typen zusammenzuarbeiten, die die Interfaces IEnumerable und IEnumerator implementieren, l ßt es der C#-Compiler zu, daß foreach auch -ber Typen iteriert, die nur eine Typ-kompatible Untermenge der Methoden und Semantiken von IEnumerable und IEnumerator zur Verf-gung stellen. Dazu wird zun chst gepr-ft, ob der Typ der in foreach-Anweisung verwendeten Collection -ber eine zug ngliche GetEnumerator()-Methode verf-gt, die keine Parameter erwartet und einen Typ zur-ckgibt, der )ffentlich zug nglich ist. Ist das der Fall, wird gepr-ft, ob dieser Typ eine hnliche Funktionalit t aufweist wie das IEnumerator-Interface. Dazu -berpr-ft der Compiler, ob der Typ eine zug ngliche MoveNext()-Methode (die keine Parameter erwartet und ein Resultat vom Typ bool zur-ckliefert) und eine zug ngliche Current-Eigenschaft besitzt (die den selben Typ hat, wie die einzelnen Elemente in der foreach-Anweisung). Wenn all diese Anforderungen erf-llt sind, erzeugt der C#-Compiler IL-Code, der diese Methoden und Typen direkt benutzt, anstatt den Umweg -ber die Interfaces zu gehen. Damit muß Client-Code kein Downcasting von object vornehmen, und bei Collections, die Werttypen zur-ckgeben, f llt auch das Boxing weg. Wenn man das vorige Beispiel f-r den beschriebenen Mechanismus umarbeitet, erh lt man folgendes:
Collections
using System.Collections; public class MyCollection { // ... int[] data; public Enumerator GetEnumerator () { return new MyCollection.Enumerator(this); } public class Enumerator { MyCollection outer; int currentIndex = -1; internal Enumerator(MyCollection outer) { this.outer = outer; } public int Current { get { if (currentIndex == outer.data.Length) throw new InvalidOperationException(); return outer.data[currentIndex]; // Kein Boxing! } } public bool MoveNext() { if (currentIndex > outer.data.Length) throw new InvalidOperationException(); return ++currentIndex < outer.data.Length; } } }
Das Problem bei diesem Vorgehen ist, daß dieser Typ, wenn er in anderen Sprachen verwendet wird, nicht als Collection erkannt wird, da er die Standard-Interfaces nicht implementiert. Gl-cklicherweise kann eine sogenannte explizite Interface-Implementierung benutzt werden, damit gleichzeitig beide Verfahren unterst-tzt werden, wie das folgende Beispiel zeigt:
Iterieren &ber Collections
129
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
using System.Collections; public class MyCollection : IEnumerable { // ... int[] data; public Enumerator GetEnumerator() { return new MyCollection.Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator; } public class Enumerator : IEnumerator { MyCollection outer; int currentIndex = -1; internal Enumerator(MyCollection outer) { this.outer = outer; } public int Current { get { if (currentIndex == outer.data.Length) throw new InvalidOperationException(); return outer.data[currentIndex]; } } public bool MoveNext() { if (currentIndex > outer.data.Length) throw new InvalidOperationException(); return ++currentIndex < outer.data.Length; } object IEnumerator.Current { get { return this.Current; } } bool IEnumerator.MoveNext() { return this.MoveNext(); } void IEnumerator.Reset() { currentIndex = -1; } } }
Dieser Ansatz erlaubt es C#-Code, den typsicheren und effizienteren Weg zu gehen, w hrend andere Sprachen wie zum Beispiel VB.NET die allgemeinere Interface-basierte Implementierung verwenden k)nnen.
Das Interface IDictionaryEnumerator Weiter unten in diesem Kapitel werden wir die Benutzung von Dictionary-Datenstrukturen erkl ren. Das Interface IDictionaryEnumerator ist die Standardschnittstelle zum Iterieren -ber eine solche Datenstruktur, in der jedes Element -ber einen Schl-ssel und einen Wert verf-gt. Die Eigenschaft Entry ist eine typsicherere Variante der Eigenschaft Current, und die Eigenschaften Key und Value erlauben den direkten Zugriff auf die Schl-ssel und Werte. Das Interface sieht wie folgt aus:
130
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
public interface IDictionaryEnumerator : IEnumerator { DictionaryEntry Entry {get;} object Key {get;} object Value {get;} }
Standard-Collection-Interfaces Obwohl IEnumerable und IEnumerator ein Standardverfahren zur Verf-gung stellen, auf die Inhalte von Collections zuzugreifen, erlauben sie es nicht, diese auch zu ver ndern oder andere oft ben)tigte Aufgaben (wie etwa das Bestimmen der Gr)ße oder das Suchen nach bestimmten Elementen) durchzuf-hren. Das .NET Framework stellt mit ICollection, IList und IDictionary drei weitere Interfaces zur Verf-gung, die Collections implementieren sollten, um diese Arten von Operationen zu unterst-tzen.
Das Interface ICollection Das Interface ICollection ist die Standardschnittstelle f-r abz hlbare Collections von Objekten. Es erm)glicht es, die Gr)ße der Collection zu bestimmen und zu entscheiden, ob die Collection modifiziert und synchronisiert werden kann. Mit diesem Interface kann man auch den Inhalt einer Collection zur sequentiellen Bearbeitung in ein Array kopieren. Da ICollection das Interface IEnumerable erweitert, k)nnen Collections, die ICollection implementieren, ebenfalls mit IEnumerable und IEnumerator traversiert werden. Das Interface sieht wie folgt aus: public interface ICollection : IEnumerable { void CopyTo(Array array, int index); int Count {get;} bool IsReadOnly {get;} bool IsSynchronized {get;} object SyncRoot {get;} }
Das Interface IList ist die Standardschnittstelle f-r Collections, die wie Arrays indiziert werden k)nnen. Zus tzlich zu der bereits in ICollection und IEnumerable enthaltenen Funktionalit t erlaubt es IList, die Collection (mit dem -berladenen Indexer) direkt -ber die Position zu indizieren, Elemente anhand ihrer Position innerhalb der Collection hinzuzuf-gen, zu entfernen und zu modifizieren und Elemente innerhalb der Collection zu suchen. Das Interface sieht wie folgt aus: public interface IList : ICollection, IEnumerable { object this[int index] {get; set} int Add(object o); void Clear(); bool Contains(object value); int IndexOf(object value); void Insert(int index, object value);
Standard-Collection-Interfaces
131
Collections
Das Interface IList
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
void Remove(object value); void RemoveAt(int index); }
Das Interface IDictionary Das Interface IDictionary ist die Standardschnittstelle f-r Schl-ssel/Wert-basierte Collections wie etwa Hashtables, Maps und Property-Bags. Phnlich wie IList stellt es die in ICollection und IEnumerable bereits enthaltenen Funktionen zur Verf-gung und dar-ber hinaus noch solche zum Zugriff auf oder zum Entfernen von Elementen -ber den Schl-ssel, zur Suche nach einem bestimmten Element in der Collection und zum Zugriff auf die Listen aller bereits in der Collection enthaltenen Schl-ssel oder Werte. Das Interface sieht wie folgt aus: public interface IDictionary : ICollection, IEnumerable { object this[object key] {get; set}; ICollection Keys {get;} ICollection Values {get;} void Clear(); bool Contains(object value); IDictionaryEnumerator GetEnumerator(); void Remove(object key); }
Vordefinierte Collection-Klassen Die FCL besitzt einen relativ umfangreichen Satz an vordefinierten Datenstrukturen, die konkrete Implementierung f-r alle in diesem Kapitel vorgestellten Interfaces bereitstellen. Da C# jedoch noch keine generischen Typen unterst-tzt, arbeiten die Implementierung mit dem Typ object, was dieselben Nachteile (h ufiges Casten, Boxing und Unboxing von Wert-Typen) mit sich bringt, die bereits bei der Beschreibung des Interfaces IEnumerator angesprochen wurden. Wenn man auf typsicherere Collection-Klassen Wert legt, kann man die vordefinierten Typen als Grundlage f-r eigene typsichere Varianten benutzen.
Die Klasse Array Die Klasse Array ist die grundlegende zusammengesetzte Datenstruktur in der FCL. Sie stellt ein Array fester Gr)ße dar, das Objektreferenzen eines bestimmten Typs enth lt. Da die Datenstruktur Array grundlegend ist, stellt die Sprache C# Mechanismen zur expliziten Deklaration und Initialisierung zur Verf-gung (mehr Informationen dazu finden Sie in Kapitel 2 und 3). Der Speicher f-r ein Array wird auf dem GC-Heap alloziert, wenn eine Klasse instantiiert wird, und kann sich danach nicht mehr ndern (vergleiche die Klasse ArrayList f-r eine gr)ßenver nderliche Array- hnliche Datenstruktur). Die Klasse Array implementiert ICollection, IList und IEnumerable. Damit k)nnen Arrays wie Listen, generische klonbare Collections oder Mengen von Elementen behandelt werden, die durchlaufen werden k)nnen. Zus tzlich unterst-tzt die Klasse Array das Sortieren und Suchen innerhalb des Arrays. H ufig wird das Sortieren durchgef-hrt, indem es an die enthaltenen Objekte und ihre Implementierung von IComparable delegiert wird. Das setzt voraus, daß Typen, die f-r die Benutzung in Arrays vorgesehen sind, auch IComparable implementieren. Im folgenden sehen Sie ein Beispiel f-r die Benutzung von Array:
132
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
string[] strs1 = { "heute", "ist", "das", "datum" }; Array.Reverse(strs1); Array strs2 = Array.CreateInstance(typeof(string), 4); strs2.SetValue("fFr", 0); strs2.SetValue("alle", 1); strs2.SetValue("guten", 2); strs2.SetValue("menschen", 3); Array strings = Array.CreateInstance(typeof(string), 8); Array.Copy(strs1, strings, 4); strs2.CopyTo(strings, 4); foreach (string s in strings) Console.WriteLine(s);
Die Klasse ArrayList Die Klasse ArrayList stellt ein dynamisch gr)ßenver nderliches Array von Objekten dar, die das Interface IList implementieren. Eine ArrayList arbeitet intern mit einem Array von Elementen, das durch ein gr)ßeres ersetzt wird, wenn seine Kapazit tsgrenze erreicht wird. Es ist beim Anh ngen von Elementen sehr effizient (da es fast immer einen freien Platz am Ende gibt), aber ineffizient, wenn Elemente eingef-gt werden (da alle Elemente ab der Position, an der eingef-gt werden soll, um eine Position verschoben werden m-ssen). Die Suche darin kann effizient sein, wenn die Methode BinarySearch() auf eine ArrayList angewendet wird, die vorher sortiert wurde. Anderenfalls ist das Suchen in einer ArrayList ineffizient, da jedes Element einzeln betrachtet werden muß. Der folgende Code ist ein Beispiel f-r die Benutzung der Klasse ArrayList:
Collections
ArrayList a = new ArrayList(); a.Add("Vernon"); a.Add("Corey"); a.Add("William"); a.Add("Muzz"); a.Sort(); for (int i = 0; i < a.Count; i++) Console.WriteLine(a[i]);
Die Klasse Hashtable Eine Hashtable ist eine Standard-Dictionary-Datenstruktur (Schl-ssel/Wert), die einen HashAlgorithmus benutzt, um Index-Werte effizient zu speichern. Dieser Algorithmus wird von der Methode GetHashCode() f-r jedes System.Object berechnet. Typen, die in einer Hashtable gespeichert werden sollen, sollten daher GetHashCode() -berladen, um einen ad quaten Hash f-r die im Objekt enthaltenen Daten zu liefern. Hashtable implementiert außerdem das Interface IDictionary und kann also auch als normale Dictionary-Datenstruktur behandelt werden. Das folgende ist ein Beispiel f-r die Benutzung der Klasse Hashtable: Hashtable ht = new Hashtable(); ht["Eins"] = 1; ht["Zwei"] = 2; ht["Drei"] = 3; Console.WriteLine(ht["Zwei"]); // Gibt "2" aus
Vordefinierte Collection-Klassen
133
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Die Klasse Queue Eine Queue stellt eine Standard-FIFO-Datenstruktur (first-in first-out) dar, die unter anderem Funktionen zur Verf-gung stellt, um ein Objekt in die Queue einzureihen, aus ihr herauszunehmen und um ihr oberstes Element anzusehen. Der folgende Code ist ein Beispiel f-r die Benutzung von Queue: Queue q = new Queue(); q.Enqueue(1); q.Enqueue(2); Console.WriteLine(q.Dequeue()); // Gibt "1" aus Console.WriteLine(q.Dequeue()); // Gibt "2" aus
Die Klasse Stack Ein Stack stellt eine Standard-LIFO-Datenstruktur (last-in first out) dar, die einfache Methoden zum Ablegen eines Elements auf dem Stapel (Push) und zum Holen des obersten Elements vom Stapel (Pop) zur Verf-gung stellt. Der folgende Code ist ein Beispiel f-r die Benutzung der Klasse Stack: Stack s = new Stack(); s.Push(1); // Stack = 1 s.Push(2); // Stack = 1,2 s.Push(3); // Stack = 1,2,3 Console.WriteLine(s.Pop()); // Gibt 3 aus, Stack=1,2 Console.WriteLine(s.Pop()); // Gibt 2 aus, Stack=1 Console.WriteLine(s.Pop()); // Gibt 1 aus, Stack=
Die Klasse BitArray Ein BitArray ist ein Array dynamischer Gr)ße, das boolesche Werte enth lt. Es ist von der Speichernutzung her effizienter als ein Array von bool-Werten, da es nur ein Bit zur Darstellung jedes Wertes ben)tigt, w hrend ein Array von bool-Werten f-r jeden Wert ein Byte ben)tigt. Der folgende Code ist ein Beispiel f-r die Benutzung der Klasse BitArray: BitArray bits = new BitArray(); bits.Length = 2; bits[1] = true; bits.Xor(bits); // Exclusiv-Oder-VerknFpfung des Arrays mit sich selbst
Die Klasse SortedList Eine SortedList ist eine Standard-Dictionary-Datenstruktur, die eine bin re Suche zum effzienten Indizieren benutzt. SortedList implementiert das Interface IDictionary und wird entsprechend wie jede andere Dictionary-Datenstruktur benutzt. Außerdem implementiert SortedList auch die Interfaces ICollection und IEnumerable. Es folgt ein Beispiel f-r die Benutzung der Klasse SortedList: SortedList s = new SortedList(); s["Zebra"] = 1; s["Antilope"] = 2;
134
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
s["Elefant"] = 3; s["Giraffe"] = 4; s["Meerkatze"] = 5; s["Dachs"] = 6; s["Truthahn"] = 7; Console.WriteLine(s["Meerkatze"]); // Gibt "5" nach drei Vergleichen aus
Die Klasse StringCollection Die Klasse StringCollection ist eine Standard-Collection zum Speichern von Strings. StringCollection implementiert das Interface ICollection und kann entsprechend wie jede andere Collection-Datenstruktur benutzt werden. Der folgende Code ist ein Beispiel f-r die Benutzung der Klasse StringCollection: StringCollection sc = new StringCollection(); sc.Add("s1"); string[] sarr = {"s2", "s3", "s4"}; sc.AddRange(sarr); foreach (string s in sc) Console.Write("{0} ", s); // s1 s2 s3 s4
Die Klasse StringDictionary Die Klasse StringDictionary ist eine Dictionary-Datenstruktur zur Speicherung von Schl-ssel/ Wert-Paaren, in denen der Schl-ssel durch einen String repr sentiert wird. Die Funktionalit t dieser Klasse hnelt sehr der der Klasse Hashtable, obwohl sie als einziges Standard-Interface IEnumerable implementiert. Der folgende Code ist ein Beispiel f-r die Benutzung der Klasse StringDictionary:
Collections
StringDictionary sd = new StringDictionary(); sd["Eins"] = 1; sd["Zwei"] = 2; sd["Drei"] = 3; Console.WriteLine(sd["Zwei"]); // Gibt "2" aus
Ordnen von Instanzen Die F higkeiten zum Sortieren und Suchen in Implementierungen der Standard-Collections h ngen wesentlich von gewissen F higkeiten der enthaltenen Objekte selbst ab. Die normalsten dieser F higkeiten sind die F higkeit zum Ordnen von Objekten (sie wird zum effizienten Suchen und Sortieren benutzt) und die F higkeit zum Berechnen eines sinnvollen Hash-Wertes (um die Speicherung von und den Zugriff auf Elemente in Dicionary-Datenstrukturen wie zum Beispiel Hashtable zu beschleunigen). Wie die meisten anderen Sachen im FCL-Collections-Framework wird das durch standardisierte Interfaces und -berladene virtuelle Methoden des Typs System.Object erreicht.
Ordnen von Instanzen
135
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Das Interface IComparable Das Interface IComparable erlaubt es einem Objekt, seine Ordnungsrelation zu einem anderen Objekt desselben Typs anzuzeigen. Um das Suchen und Sortieren in selbstgeschriebenen Typen in einem Array zu erlauben, muß IComparable implementiert werden. Das Interface IComparable sieht wie folgt aus: public interface IComparable { int CompareTo(object rhs); }
Implementierungen dieses Interfaces sollten den folgenden semantischen Regeln folgen: 1.
Wenn a vor b kommt ? a.CompareTo(b) < 0
2.
Wenn a gleich b ist ? a.CompareTo(b) == 0
3.
Wenn a nach b kommt ? a.CompareTo(b) > 0
4.
null kommt zuerst: a.CompareTo(null) > 0
5.
a.CompareTo(b) ? a.GetType() == b.GetType()
Eine Beispiel-Implementierung dieses Interfaces k)nnte etwa wie folgt aussehen: public sealed class Person : IComparable { public string Name; public int Age; public int CompareTo(object o) { // Auf null prFfen if (o==null) return 1; // Auf den zu vergleichenden Typ prFfen if (o.GetType() != this.GetType()) throw new ArgumentException(); // Instanzen in aufsteigender Folge nach Alter sortieren Person rhs = (Person)o; if (Age < rhs.Age) return -1; if (Age > rhs.Age) return 1; return 0; } }
Beachten Sie, daß in diesem Beispiel Person als sealed deklariert wurde. Das geschah, um die Implementierung zu vereinfachen, die sehr kompliziert sein kann, wenn man versucht, f-r sp tere, unerwartete Ableitungen vorzusorgen. Sie sollten auch immer darauf achten, daß die Regeln in dem oben dargestellten semantischen Kontrakt eingehalten werden. Das gilt auch f-r die Regel, daß nur identische Typen verglichen werden d-rfen. Eine M)glichkeit, dies sicherzustellen, besteht darin, die Typen mit der Methode GetType() explizit zu vergleichen, wie es das Beispiel zeigt. Weiterhin sollte der Vergleich mit einer Null-Referenz keine Exception ausl)sen, sondern einen Wert gr)ßer 0 als Ergebnis liefern, wodurch die Null-Eintr ge am Anfang der Liste einordnet werden. Die Implementierung des Interfaces IComparable erlaubt es, eine nat-rliche Ordnung eines Typs zu vereinbaren. Manchmal ist es jedoch n)tig, mehr als eine Ordnung f-r einen Typ anzugeben. Das kann durch die Implementierung des Interfaces IComparer erreicht werden.
136
Kapitel 7: Collections
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Das Interface IComparer Die Collection-Klassen f-hren die Sortierung -ber eine Implementierung des Interfaces IComparer durch, das zwei voneinander unabh ngige Instanzen bez-glich ihrer Ordnungsrelation vergleicht. Das Interface IComparer sieht wie folgt aus: public interface IComparer { int Compare(object x, object y); }
Normalerweise muß dieses Interface nicht implementiert werden, da eine Standardimplementierung, die die Arbeit an das Interface IComparable weiterleitet, bereits vom Typ Comparer zur Verf-gung gestellt wird (der per Default von der Klasse Array benutzt wird). Bei Bedarf k)nnen Sie jedoch zus tzliche Implementierungen des Interfaces IComparer bereitstellen, die typspezifische Vergleiche durchf-hren. (Diese werden h ufig als innere Hilfsklassen implementiert.)
Erzeugen von Hash-Werten Alle Objekte stellen -ber die Methode GetHashCode() des Typs System.Object einen 32 Bit breiten Integer als Hash-Wert ihres Inhalts zur Verf-gung. Gute Hash-Werte k)nnen einen dramatischen Einfluß auf die Geschwindigkeit eines Hashtable haben (sie werden benutzt, um zu entscheiden, zu welchem Bucket des Hashtable ein Eintrag geh)rt). Außerdem kann man damit einen einfachen (aber schnellen) Gleichheitstest durchf-hren. Diese Benutzung von GetHashCode wird in den folgenden Beispielen demonstriert: void Enroll(Student s, CourseList cl) { hashtable.Add(s, cl); // GHC fFr SchlFssel (s) aufgerufen } bool FastCompare(Student s1, Student s2) { // Benutzen von GHC, um festzustellen, ob die M]glichkeit // zur Gleichheit besteht if (s1.GetHashCode() != s2.GetHashCode()) return false;
}
Die Implementierung von GetHashCode() in System.Object gibt eine halbwegs eindeutige Member-ID zur-ck, w hrend die Implementierung von GetHashCode() f-r System.ValueType einfach den Hash-Wert des ersten Feldes des Wert-Typs zur-ckliefert. Obwohl diese Methoden in den meisten F llen ausreichen, kann man manchmal doch durch eigene Implementierungen von GetHashCode() Performance-Gewinne erreichen. =berl dt ein Typ die Methode Equals(), ist es zwingend erforderlich, GetHashCode() ebenfalls zu -berladen. Das f-hrt dazu, daß viele Typen aus dem Framework GetHashCode(), wie hier gezeigt, -berladen: void DumpHashes(object o, int i, Version Console.WriteLine(o.GetHashCode()); // Console.WriteLine(i.GetHashCode()); // Console.WriteLine(v.GetHashCode()); // }
Erzeugen von Hash-Werten
v) { Objekt-Index Integer-Wert Hash-Wert der Felder
137
Collections
// Test, ob tatsYchlich Gleichheit return s1.Equals(s1);
H:/Oreilly/CNet_Nutshell/Cnet.3d vom 20.11.2002 Seitenformat: 172,00 x 230,00 mm
Die Methode Object.GetHashCode Die Methode System.Object.GetHashCode ist wie folgt deklariert: public virtual int GetHashCode();
Es ist wichtig, den generellen Kontrakt f-r GetHashCode() zu kennen, der wie folgt aussieht: 1.
A.GetHashCode() ? eine gleichm ßige Verteilung -ber alle as
2.
a.Equals(b) ? a.GetHashCode() == b.GetHashCode()
3.
A -berl dt GetHashCode ? A -berl dt Equals
4.
Sicherheit: keine Exceptions ausl)sen; keine zirkul ren Referenzen
Dahinter steht der Gedanke, daß eine gute Implementierung alle 32 Bit ausnutzt, um eine gleichm ßige Verteilung der Hash-Werte zu erreichen, und daß idealerweise die Bedeutung der Abfolge der Felder bewahrt wird (um sicherzustellen, daß f-r Point(10,20) einen anderer Hash-Wert errechnet wird als f-r Point(20,10)). Traditionell wird das durch die Multiplikation des Hash-Wertes jedes Feldes mit einer ungeraden Primzahl erreicht (37 ist in der Java-Welt, in der es einen hnlichen Mechanismus gibt, ein beliebter Wert daf-r). Wenn man seinen Typ nicht direkt von System.Object abgeleitet hat, kann man auch dar-ber nachdenken, die HashWerte f-r die Basisklasse mit den Hash-Werten f-r die Member des eigenen Typs zu kombinieren. Abschließend sollten Sie noch daran denken, daß bei der Berechnung des Hash-Werts f-r zusammengesetzte Datenstrukturen (wie etwa Arrays) der Inhalt nicht immer richtig verarbeitet wird, und daß dies daher von Hand erledigt werden muß. Es folgt nun ein Beispiel, das einige dieser Regeln beachtet, um eine gute Verteilung der Hash-Werte zu Erreichen, wenn alle Daten des Typs bei der Berechnung des Hash-Wertes benutzt werden. public sealed class Data { public readonly short x, y; public readonly Color c; ArrayList al = new ArrayList(); public override int GetHashCode() { int hc = 1; // base.GetHashCode falls base!=object hc = 37*hc + x