Kapitel 3 Das Typsystem von C# Korbinian Molitorisz IPD Tichy Lehrstuhl für Programmiersysteme KIT die Kooperation von Forschungszentrum Karlsruhe GmbH und Universität Karlsruhe (TH)
Agenda Kurze Wiederholung CLI-Typen Wert-/Referenztypen Boxing/Unboxing Felder Enumerationen 2
Eine virtuelle Maschine (VM) ein gemeinsames Typsystem Ziel der CLI ist die Interoperabilität verschiedener Programmiersprachen Szenario: Ein C#-Programm ruft eine Routine auf, die in Basic geschrieben ist. Ausführbar, wenn beides in Bytecode für die selbe VM übersetzt wurde. Der VM ist schließlich egal, woher der Bytecode kommt und wie er erzeugt wurde. Annahme: Die Basic-Routine erwartet einen ganzzahligen Parameter. Wie viele Bits/Bytes sind auf den Keller zu legen? 16 oder 32? Wie sind diese Bits zu interpretieren? (Vorzeichen, Big-Endian/Little- Endian) Lösung für die CLI: Ein einheitliches Typsystem für alle Programmiersprachen. 3
Klassifikation der Typen Alle Typen Werttypen Referenztypen Enumerationen Strukturen Klassen Felder Schnittstellen Delegierer benutzerdefinierbar Einfache Typen sbyte 8-bit signierte Ganzzahl short 16-bit signierte Ganzzahl int 32-bit signierte Ganzzahl long 64-bit signierte Ganzzahl byte 8-bit unsignierte Ganzzahl ushort 16-bit unsignierte Ganzzahl uint 32-bit unsignierte Ganzzahl ulong 64-bit unsignierte Ganzzahl float einfach-präzise Gleitkommazahl double doppelt-präzise Gleitkommazahl bool Bool scher Wert char Unicode-Zeichen decimal Präzise Gleitkomma-Dezimalzahl mit mindestens 28 signifikanten Stellen Speicherbedarf: 128 Bit Darstellungsraum: ( 1) s c 1 -e mit s {,1, c C max 1 28 und E min e E max 28. Siehe 11.1.7 8.2.4 4
Implizite Konvertierungen einfacher Typen 8 Bit byte sbyte Gleitkomma, mit Vorzeichen 16 Bit char ushort short Diese Konvertierungen sind möglicherweise mit einem Verlust von Präzision verbunden. Die Größenordnung der Zahl bleibt aber erhalten. 32 Bit 64 Bit Entspricht nicht dem tatsächlichen Konvertierungsweg! Für Details siehe 13.1.2 uint ulong int long float double 128 Bit Implizite Konvertierung, transitiv decimal vorzeichenlos mit Vorzeichen 5
Werttypen vs. Referenztypen Werttyp Referenztyp Variable enthält Wert Referenz auf den Wert Zuweisung Kopiert den Wert Kopiert die Referenz Initialisierung,., false, '\' null Speicherort der Nutzdaten An Ort und Stelle im Aufrufkeller im Objekt (Halde) im Feld (Halde) woanders theoretisch auf der Halde praktisch auch manchmal im Keller ( Fluchtanalyse) 6
Allgemeinster Typ: object Der Obertyp aller Wert- und Referenztypen ist object. Einer Variablen vom Typ object kann alles zugewiesen werden. Alle Methoden von System.Object können auf jeder Variable aufgerufen werden: Equals Stellt fest, ob zwei Instanzen gleich sind. GetHashCode Hashfunktion, eignet sich für die Verwendung in Hashalgorithmen und Hashdatenstrukturen, z. B. in einer Hashtabelle. GetType Ruft den Typ der aktuellen Instanz ab. ToString Gibt eine Zeichenkette zurück, die das aktuelle Objekt repräsentiert. 8.2.4 7
Allgemeinster Typ: object Beispiel class Program { private static double π = 3.14; private static int x = 314; private static char pi = 'π'; private static string hello = "Hello World!"; static void Main(string[] args) { object o; o = π; Console.WriteLine("\u3C=" + o.tostring()); o = x; Console.WriteLine("\u3C=" + o.tostring()); o = pi; Console.WriteLine("\u3C=" + o.tostring()); o = hello; Console.WriteLine("\u3C=" + o.tostring()); π=3,14 π=314 π=π π=hello World! 8.2.4 8
Ein- und Auspacken (boxing und unboxing) Ziel: auf einen Wert so zugreifen, wie auf ein Objekt Idee: betrachte diesen Wert als ein Objekt mit einem einzigen Attribut, dass diesen Wert hält Wenn der Wert geschrieben werden soll, verändere implizit den Wert dieses Attributes Wenn der Wert gelesen werden soll, lese implizit den Wert dieses Attributes und gib diesen zurück Überlegung: Kann man leicht für alle Werttypen anbieten. 8.2.4 9
Ein- und Auspacken (boxing und unboxing) Beispiel: object obj = 3; packt den Wert 3 in ein Objekt ein: class IntContainer { int val; IntContainer(int x) {val = x; object obj = new IntContainer(3); So könnte der Code aussehen, der für den obigen Befehl erzeugt wird. 3 obj 3 8.2.4 1
Ein- und Auspacken (boxing und unboxing) Beispiel in C#: class Test { static void Main() { int i = 123; object o = i; // boxing if (o is int) { Console.Write(o.GetType()); int j = (int) o; // unboxing gibt System.Int32 aus Integration des Ein- und Auspackmechanismus in C# ist transparent. prüft, ob o vom Typ int ist 8.2.4 11
Einwickeln und Initialisierung Was passiert hier? Fehlermeldung in Zeile 2: Operator '==' cannot be applied to operands of type 'object' and 'int class Program { private static object o; static void Main(string[] args) { 1. if (o == null) Console.WriteLine("true"); else Console.WriteLine("false"); 2. if (o == ) Console.WriteLine("true"); else Console.WriteLine("false"); 3. if (o == false) Console.WriteLine("true"); else Console.WriteLine("false"); 4. if (o == '\') Console.WriteLine("true"); else Console.WriteLine("false"); A) Das Programm wird übersetzt und gibt true false false false aus. B) Das Programm wird übersetzt und gibt true true true true aus. C) Das Programm wird nicht übersetzt. D) Das Programm wird übersetzt und bricht mit einer Fehlermeldung in Zeile 2. ab. 12
Felder Eindimensionale Felder sind in C# genau wie in Java: Neues Feld anlegen: int[] a = new int[17]; Neues Feld mit vorbelegten Werten erzeugen: int[] b = new int[] {1,2,3,4,5; Kurzschreibweise für b: int[] c = {1,2,3,4,5; Länge eines Feldes ermitteln: c.length; 8.2.3 13
Felder Feld von Werttypen: int[] x = new int[4]; x[1] = 7; Feld von Referenztypen: object[] y = new object[4]; y[1] = 7; x = $93 7 y = $26 null $41 null null null 7 8.2.3 14
Mehrdimensionale Felder Wie in Java: ausgefranste Felder (jagged arrays) Feld von Feldern" int[][] x = new int[3][]; x[] = new int[4]; x[1] = new int[5]; x[2] = new int[2]; Länge der 2. Dim. ermitteln: x[1].length; Blockfelder (rechteckig) int[,] y = new int[4,3]; x = Auch höher dimensional: int[,,,] z; $26 $66 null $41 null $73 null Länge der 2. Dim. ermitteln: z.getlength(1); y = $93 15
Kovarianz bei Feldern Aus 19.5: Kovarianz bei Feldern: Wenn Referenztyp B implizit in Referenztyp A konvertierbar ist, dann ist B[D] implizit in A[D] konvertierbar. Dabei ist D eine Dimensionsangabe, die in beiden Fällen gleich sein muss (egal ob ausgefranst oder rechteckig). Was passiert hier? class A { class B : A { class Program { static void Main() { A[,,] a; B[,,] b = new B[2, 3, 4]; a = b; a[,, ] = new B(); a[1, 1, 1] = new A(); Laufzeitfehler(!) in der Zeile a[1, 1, 1] = new A(); A B 16
Enumerationen Enumerationen definieren einen Typnamen für eine Menge von benannten, symbolischen Konstanten Werden in Multiple-Choice-Situationen verwendet, in der zur Laufzeit eine Auswahl aus einer festen (zur Übersetzungszeit bekannten) Menge von Möglichkeiten getroffen wird Beispiel: enum Color { Red, Blue, Green class Shape { public void Fill(Color color) { switch(color) { case Color.Red: break; case Color.Blue: break; case Color.Green: break; default: break; 8.11 17
Enumerationen Syntax (Auszug): enum-declaration ::= enum-modifier * enum identifier [ enum-base ] enum-body [ ; ] enum-base ::= : integral-type System.Enum enum-body ::= { enum-member-decl (, enum-member-decl )* enum-modifier ::= new public protected internal private enum-member-decl ::= identifier [ = constant-expression ] Beispiele: enum Color { Red, Green, Blue enum Color { Red, Green = 1, Blue enum Color { Red, Green, Blue, Max = Blue enum Color : sbyte { Red = -1, Green = -2, Blue = -3 Jede Enumeration erbt automatisch von System.Enum Red = (int) Green = 1 (int) Blue = 11 (int) object System.ValueType Red = (int) Green = 1 (int) Blue = 2 (int) Red = (int) Green = 1 (int) Blue = 2 (int) Max = 2 (int) Red = -1 (sbyte) Green = -2 (sbyte) Blue = -3 (sbyte) 21 18
Enumerationen Enumerationsvariablen fühlen sich im C#-Programm wie normale Ganzzahlvariablen an. Unterstützte Operatoren: ==,!=, <, >, <=, >=, +, -, ^, &,, ~, ++, --, sizeof Der Wertebereich wird nicht überprüft! Aber: Eine Enumeration ist ein eigener Typ und verlangt explizite Konvertierungen: Gegeben: enum Color { Red, Green, Blue Geht nicht: int i = Color.Blue; Color c = 1; Geht: int i = (int)color.blue; Color c = (Color)1; Geht auch (!): Color c = (Color)513; 21 19
Was passiert hier? enum Color { Red, Green = 3, Blue ; static void Main(string[] args) { Color c = (Color) 4; Console.WriteLine(c); Und jetzt? Erste Ausgabe: Blue Zweite Ausgabe: 5 static void Main(string[] args) { Color c = (Color) 5; Console.WriteLine(c); 2