Galileo Computing <openbook>
Galileo Computing - Programming the Net
Galileo Computing - Programming the Net


C# von Eric Gunnerson
Die neue Sprache für Microsofts .NET-Plattform
C# - Zum Katalog
gp Kapitel 31 Überblick über die .NET-Frameworks
  gp 31.1 Numerische Formatierung
  gp 31.2 Formatierung von Datum und Uhrzeit
  gp 31.3 Benutzerdefinierte Objektformatierung
  gp 31.4 Numerische Syntaxanalyse
  gp 31.5 XML-Verwendung in C#
  gp 31.6 Eingabe/Ausgabe
  gp 31.7 Serialisierung
  gp 31.8 Threading
  gp 31.9 Lesen von Webseiten

Kapitel 31 Überblick über die .NET-Frameworks

Die .NET-Frameworks enthalten viele Funktionen, die üblicherweise in sprachspezifischen Laufzeitbibliotheken enthalten sind. Es ist daher unerlässlich, die in den Frameworks verfügbaren Klassen zu kennen.


Galileo Computing

31.1 Numerische Formatierung  downtop

Numerische Typen werden über die Mitgliedsfunktion Format() des jeweiligen Datentyps formatiert. Diese Funktion kann über String.Format() direkt aufgerufen werden, wodurch die Format()-Funktion für jeden Datentyp aufgerufen wird, oder über Console.WriteLine(), mit der String.Format() aufgerufen wird.

Die Formatierung benutzerdefinierter Objekte wird im Abschnitt »Benutzerdefinierte Objektformatierung« weiter unten in diesem Kapitel besprochen. Im vorliegenden Abschnitt wird die Formatierung integrierter Typen erläutert.

Es gibt zwei Methoden zur Festlegung der numerischen Formatierung. Mit Hilfe einer Standardformatzeichenfolge kann ein numerischer Typ in eine Zeichenfolgendarstellung konvertiert werden. Ist eine weiterführende Steuerung der Ausgabe erforderlich, kann eine benutzerdefinierte Formatzeichenfolge verwendet werden.


Galileo Computing

31.1.1 Standardformatzeichenfolgen  downtop

Eine Standardformatzeichenfolge umfasst ein Zeichen, mit dem das Format angegeben wird, gefolgt von einer Ziffernsequenz, mit der die Genauigkeit festgelegt wird. Es werden die folgenden Formate unterstützt:

Formatzeichen Beschreibung
C, c Währung
D, d Dezimal
E, e Wissenschaftlich (exponentiell)
F, f Festkomma
G, g Allgemein
N, n Zahl
X, x Hexadezimal

Währung

Die Währungsformatzeichenfolge konvertiert einen numerischen Wert in eine Zeichenfolge mit einem Währungswert für eine Ländereinstellung. Standardmäßig werden die Formatinformationen durch die aktuelle Ländereinstellung bestimmt, können aber durch Übergeben eines NumberFormatInfo-Objekts geändert werden.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:C}", 33345.8977);
        Console.WriteLine("{0:C}", -33345.8977);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

$33,345.90
($33,345.90)

Dezimal

Die Dezimalformatzeichenfolge konvertiert den numerischen Wert in eine Ganzzahl. Die Mindestzahl an Kommastellen wird durch den Genauigkeitsbezeichner festgelegt. Das Ergebnis wird von links mit Nullen aufgefüllt, bis die erforderliche Stellenzahl erreicht ist.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:D}", 33345);
        Console.WriteLine("{0:D7}", 33345);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

33345
0033345

Wissenschaftlich (exponentiell)

Die wissenschaftliche Formatzeichenfolge (exponentiell) konvertiert den Wert in eine Zeichenfolge der Form

m.dddE+xxx

Vor dem Dezimalkomma befindet sich immer eine Stelle, die Anzahl der Dezimalstellen wird durch den Genauigkeitsbezeichner festgelegt und umfasst standardmäßig sechs Stellen. Der Formatbezeichner steuert, ob in der Ausgabe E oder e erscheint.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:E}", 33345.8977);
        Console.WriteLine("{0:E10}", 33345.8977);
        Console.WriteLine("{0:e4}", 33345.8977);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

3.334590E+004
3.3345897700E+004
3.3346e+004

Festkomma

Die Festkommaformatzeichenfolge konvertiert den Wert in eine Zeichenfolge, bei der die Anzahl der Stellen nach dem Dezimalkomma durch den Genauigkeitsbezeichner festgelegt wird.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:F}", 33345.8977);
        Console.WriteLine("{0:F0}", 33345.8977);
        Console.WriteLine("{0:F5}", 33345.8977);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

33345.90
33346
33345.89770

Allgemein

Die allgemeine Formatzeichenfolge konvertiert den Wert entweder in das Festkomma- oder das wissenschaftliche Format, je nachdem, welches kompakter ist.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:G}", 33345.8977);
        Console.WriteLine("{0:G7}", 33345.8977);
        Console.WriteLine("{0:G4}", 33345.8977);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

33345.8977
33345.9
3.335E4

Zahl

Die Zahlenformatzeichenfolge konvertiert den Wert in eine Zahl mit eingebetteten Kommata, beispielsweise

12,345.11

Das Format kann durch Übergeben eines NumberFormatInfo-Objekts an die Format()-Funktion gesteuert werden.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:N}", 33345.8977);
        Console.WriteLine("{0:N4}", 33345.8977);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

33,345.90
33,345.8977

Hexadezimal

Die hexadezimale Formatzeichenfolge konvertiert den Wert in das Hexadezimalformat. Die Mindestzahl an Kommastellen wird durch den Genauigkeitsbezeichner festgelegt, die Zahl wird zum Erreichen dieser Stellenzahl mit Nullen aufgefüllt.

Das Verwenden von X führt zu Großbuchstaben im konvertierten Wert; x führt zur Kleinbuchstabenversion.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:X}", 255);
        Console.WriteLine("{0:x8}", 1456);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

FF
000005b0

NumberFormatInfo

Die NumberFormatInfo-Klasse wird zum Steuern der Zahlenformatierung eingesetzt. Durch das Setzen der Eigenschaften in dieser Klasse kann der Programmierer das Währungssymbol, das Dezimaltrennzeichen und andere Formatierungseigenschaften steuern.


Galileo Computing

31.1.2 Benutzerdefinierte Formatzeichenfolgen  downtop

Über die benutzerdefinierten Formatzeichenfolgen erhält der Programmierer umfangreichere Steuerungsmöglichkeiten über die Konvertierung, als dies mit den Standardformatzeichenfolgen möglich ist. Bei den benutzerdefinierten Formatzeichenfolgen bilden Sonderzeichen die Vorlage für die Formatierung der betreffenden Zahl. Alle Zeichen, die in der Formatzeichenfolge keine besondere Bedeutung aufweisen, werden wörtlich in den Ausdruck kopiert.

Stellen- oder Nullplatzhalter

Das Nullzeichen (0) wird als Stellen- oder Nullplatzhalter verwendet. Verfügt der numerische Wert an der Position über eine Stelle, an der in der Formatzeichenfolge die 0 erscheint, erscheint diese Stelle im Ergebnis. Falls nicht, erscheint an dieser Stelle eine Null.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:000}", 55);
        Console.WriteLine("{0:000}", 1456);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

055
1456

Stellen- oder Leerplatzhalter

Das Rautenzeichen (#) wird als Stellen- oder Leerplatzhalter verwendet. Die Raute funktioniert genau wie der Nullplatzhalter (0), abgesehen davon, dass ein Leerzeichen erscheint, wenn die Stelle nicht belegt ist.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:#####}", 255);
        Console.WriteLine("{0:#####}", 1456);
        Console.WriteLine("{0:###}", 32767);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

255
1456
32767

Dezimalkomma

Der erste Punkt (.) in der Formatzeichenfolge legt die Position des Dezimaltrennzeichens im Ergebnis fest. Das in der formatierten Zeichenfolge als Dezimaltrennzeichen verwendete Zeichen wird durch eine NumberFormatInfo-Instanz gesteuert.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:#####.000}", 75928.3);
        Console.WriteLine("{0:##.000}", 1456.456456);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

75928.300
1456.456

Gruppentrennzeichen

Das Komma (,) wird als Gruppentrennzeichen eingesetzt. Wenn ein Komma in der Mitte eines Anzeigestellenplatzhalters und links neben dem Dezimalkomma (falls vorhanden) erscheint, wird ein Gruppentrennzeichen in die Zeichenfolge eingefügt. Das in der formatierten Zeichenfolge verwendete Zeichen und die Anzahl der zu gruppierenden Zahlen wird durch eine NumberFormatInfo-Instanz gesteuert.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:##,###}", 2555634323);
        Console.WriteLine("{0:##,000.000}", 14563553.593993);
        Console.WriteLine("{0:#,#.000}", 14563553.593993);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

2,555,634,323
14,563,553.594
14,563,553.594

Vorskalierung von Zahlen

Mit dem Kommazeichen (,) kann auch angegeben werden, dass die Zahl vorskaliert werden soll. Bei dieser Verwendung muss das Komma direkt vor dem Dezimalkomma oder am Ende der Formatzeichenfolge eingefügt werden.

Für jedes Komma an dieser Position wird die Zahl vor der Formatierung durch 1 000 geteilt.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:000,.##}", 158847);
        Console.WriteLine("{0:000,,,.###}", 1593833);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

158.85
000.002

Prozentnotierung

Mit dem Prozentzeichen (%) wird angegeben, dass die anzuzeigende Zahl als Prozentwert dargestellt werden soll. Die Zahl wird vor der Formatierung mit 100 multipliziert.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:##.000%}", 0.89144);
        Console.WriteLine("{0:00%}", 0.01285);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

89.144%
01%

Exponentielle Notierung

Wenn in der Formatzeichenfolge direkt nach einem #- oder einem 0-Platzhalter E+0, E-0, e+0 oder e-0 erscheinen, wird die Zahl in exponentieller Notierung formatiert. Die Anzahl der Stellen im Exponenten wird durch die Anzahl der 0-Platzhalter gesteuert, die im Exponentenbezeichner erscheinen. Das E oder e wird direkt in die formatierte Zeichenfolge kopiert, ein + bedeutet, dass an dieser Stelle ein Plus- oder Minuszeichen erscheint, ein – bedeutet, dass dort nur ein Zeichen vorhanden ist, wenn die Zahl negativ ist.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:###.000E-00}", 3.1415533E+04);
        Console.WriteLine("{0:#.0000000E+000}", 2.553939939E+101);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

314.155E-02
2.5539399E+101

Abschnittstrennzeichen

Das Semikolon (;) wird dazu verwendet, verschiedene Formatzeichenfolgen für eine Zahl festzulegen, je nachdem, ob die Zahl positiv, negativ oder gleich Null ist. Sind nur zwei Abschnitte vorhanden, gilt der erste Abschnitt für positive und Nullwerte, der zweite Abschnitt wird auf negative Werte angewendet. Sind drei Abschnitte vorhanden, werden diese auf positive Werte, Nullwerte und negative Werte angewendet.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:###.00;0;(###.00)}", -456.55);
        Console.WriteLine("{0:###.00;0;(###.00)}", 0);
        Console.WriteLine("{0:###.00;0;(###.00)}", 456.55);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

457
(.00)
456.55

Escapezeichen und Literale

Der umgekehrte Schrägstrich (\) kann angegeben werden, damit Zeichen nicht als Formatierungszeichen interpretiert werden. Da der umgekehrte Schrägstrich in C#-Literalen bereits eine Bedeutung trägt, ist es einfacher, die Zeichenfolge mit Hilfe der wörtlichen Literalsyntax anzugeben; andernfalls ist ein doppelter umgekehrter Schrägstrich (\\) zur Erzeugung eines einzelnen umgekehrten Schrägstriches in der Ausgabezeichenfolge erforderlich.

Sie können eine Zeichenfolge, deren Zeichen nicht interpretiert werden sollen, auch in einfache Anführungszeichen einschließen, diese Methode ist möglicherweise einfacher als die Verwendung von \.

using System;
class Test
{
    public static void Main()
    {
        Console.WriteLine("{0:###\\#}", 255);
        Console.WriteLine(@"{0:###\#}", 255);
        Console.WriteLine("{0:###'#0%;'}", 1456);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

255#
255#
1456#0%;

Galileo Computing

31.2 Formatierung von Datum und Uhrzeit  downtop

Die Klasse DateTime stellt flexible Formatierungsoptionen bereit. Es können verschiedene Einzelzeichenformate angegeben werden, des Weiteren wird die benutzerdefinierte Formatierung unterstützt.

Standardmäßige DateTime-Formate

Zeichen Muster Beschreibung
d MM/dd/yyyy ShortDatePattern
D dddd, MMMM dd, yyy LongDatePattern
f dddd, MMMM dd, YYYY HH:mm Full (langes Datum + kurze Uhrzeit)
F dddd, MMMM dd, yyyy HH:mm:ss FullDateTimePattern (langes Datum + lange Uhrzeit)
g MM/dd/yyyy HH:mm General (kurzes Datum + kurze Uhrzeit)
G MM/dd/yyyy HH:mm:ss General (kurzes Datum + lange Uhrzeit)
m, M MMMM dd MonthDayPattern
r, R ddd, dd MMM yy HH’:’mm’:’ss ’GMT’ RFC1123Pattern
s yyyy-MM-dd HH:mm:ss SortableDateTimePattern (ISO 8601)
S YYYY-mm-DD hh:MM:SS GMT Sortierbar mit Zeitzoneninformationen
t HH:mm ShortTimePattern
T HH:mm:ss LongTimePattern
u yyyy-MM-dd HH:mm:ss Wie »s«, aber mit Greenwich-Zeit, nicht der lokalen Zeit
U dddd, MMMM dd, yyyy HH:mm:ss UniversalSortableDateTimePattern

Benutzerdefinierte DateTime-Formate

Zur Erstellung eines benutzerdefinierten Formats können folgende Muster eingesetzt werden:

Muster Beschreibung
d Tag des Monats als Zahl, ohne vorangestellte Null bei einstelligen Tagen
dd Tag des Monats als Zahl, mit vorangestellter Null bei einstelligen Tagen
ddd Tag der Woche als aus drei Buchstaben bestehende Abkürzung
dddd Tag der Woche mit vollständigem Namen
M Monat als Zahl, ohne vorangestellte Null bei einstelligen Monaten
MM Monat als Zahl mit vorangestellter Null
MMM Monat als aus drei Buchstaben bestehende Abkürzung
MMMM Monat mit vollständigem Namen
y Jahr als zweistellige Zahl (letzte zwei Ziffern), keine vorangestellte Null
yy Jahr als zweistellige Zahl (letzte zwei Ziffern), mit vorangestellter Null
yyyy Jahr in vierstelliger Darstellung

Die Namen der Tage und Wochen werden durch das geeignete Feld in der DateTimeFormatInfo-Klasse festgelegt.


Galileo Computing

31.3 Benutzerdefinierte Objektformatierung  downtop

In einigen der bisher verwendeten Beispiele wurde die ToString()-Funktion außer Kraft gesetzt, um eine Zeichenfolgendarstellung einer Funktion bereitzustellen. Ein Objekt kann verschiedene Formate bereitstellen, indem die IFormattable-Schnittstelle definiert wird. Anschließend kann die Darstellung basierend auf der Zeichenfolge der Funktion geändert werden.

Eine Employee-Klasse könnte beispielsweise durch eine andere Formatzeichenfolge zusätzliche Informationen bereitstellen.

using System;
class Employee
{
    public Employee(int id, string firstName, string lastName)
    {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }
     public string Format (string format, IServiceObjectProvider sop)
    {
        if (format.Equals("F"))
            return(String.Format("{0}: {1}, {2}",
                id, lastName, firstName));
        else
            return(id.Format(format, sop));
    }
    int    id;
    string    firstName;
    string    lastName;
}
class Test
{
    public static void Main()
    {
        Employee fred = new Employee(123, "Fred", "Morthwaite");
        Console.WriteLine("No format: {0}", fred);
        Console.WriteLine("Full format: {0:F}", fred);
    }
}

Die Format()-Funktion sucht nach dem F-Format. Ist dieses vorhanden, werden die vollständigen Informationen ausgeschrieben. Kann dieses Formatzeichen nicht ermittelt werden, wird das Standardobjektformat verwendet.

Die Main()-Funktion übergibt das Formatflag im zweiten WriteLine()-Aufruf.


Galileo Computing

31.3.1 Neue Formatierung für vorhandene Typen  downtop

Es ist auch möglich, vorhandenen Objekten ein neues Format zuzuweisen. Beim folgenden Objekt können float- und double-Werte auch in Ångström formatiert werden. Ein Ångström entspricht 10E-10 Metern.

using System;
public class AngstromFormatter: IServiceObjectProvider, ICustomFormatter
{
    public object GetServiceObject(Type service)
    {
        if (service == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }
    public string Format(string format, object arg,
        IServiceObjectProvider sop)
    {
        if (format == null)
            return(String.Format("{0}", arg));

        if (format.StartsWith("Ang"))
        {
                // Zusätzliche Formatierungsinformationen
                // nach "Ang" hier extrahieren…
            string extra = "";

            if (arg is float)
            {
                float f = (float) arg;
                f *= 1.0E10F;
                return(String.Format("{0:" + extra + "}", f) + " Å");
            }
            else if (arg is double)
            {
                double d = (double) arg;
                d *= 1.0E10D;
                return(String.Format("{0:" + extra + "}", d) + " Å");
            }
        }
            // Kein unterstütztes Objekt oder Format
        return(String.Format("{0:" + format + "}", arg));
    }
}
class Test
{
    public static void Main()
    {
        AngstromFormatter angstrom = new AngstromFormatter();

        Console.WriteLine("Meters: {0}", 1.35E-8F, angstrom);
        Console.WriteLine(String.Format("Angstroms: {0:Ang}",
            new object[] {1.35E-8F}, angstrom));
        Console.WriteLine(String.Format("Angstroms: {0:Ang:g}",
            new object[] {3.59393E-9D}, angstrom));
    }
}

In diesem Beispiel unterstützt die AngstromFormatter-Klasse das Formatieren von Zahlen in Ångström, indem die Werte durch 10E-10 dividiert werden und anschließend das Ångström-Symbol »Å« an die Zeichenfolge gehängt wird. Nach dem Ang-Format können weitergehende Formatierungsinformationen angegeben werden, um die Anzeige der Gleitkommazahl zu steuern.


Galileo Computing

31.4 Numerische Syntaxanalyse  downtop

Zahlen werden mit Hilfe der für numerische Datentypen verfügbaren Parse()-Methode analysiert. Durch Übergeben von Flags der Klasse NumberStyle können die erlaubten Ausdrucksweisen angegeben werden, mit einer Instanz von NumberFormatInfo kann die Syntaxanalyse gesteuert werden.

Bei den über die standardmäßigen Formatbezeichner erzeugten numerischen Zeichenfolgen (ausgeschlossen der Hexadezimalformatbezeichner) verläuft die Syntaxanalyse immer erfolgreich, wenn NumberStyles.Any angegeben wird.

using System;
class Test
{
    public static void Main()
    {
        int value = Int32.Parse("99953");
        double dval = Double.Parse("1.3433E+35");
        Console.WriteLine("{0}", value);
        Console.WriteLine("{0}", dval);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

99953
1.3433E35

Galileo Computing

31.5 XML-Verwendung in C#  downtop

Obwohl C# die XML-Dokumentation unterstützt (siehe Kapitel 31, C# im Detail, Abschnitt »XML-Dokumentation«), bietet C# keine Sprachunterstützung für die Verwendung von XML.

Das ist aber okay, denn die Common Language Runtime bietet umfangreiche XML-Unterstützung. In diesem Themenbereich sind besonders die Namespaces System.Data.Xml und System.Xml von Interesse.


Galileo Computing

31.6 Eingabe/Ausgabe  downtop

Die .NET Common Language Runtime stellt über den Sytem.IO-Namespace E/A-Funktionen bereit. Dieser Namespace enthält Klassen für die Ein- und Ausgabe sowie für E/A-bezogene Funktionen, beispielsweise für das Durchsuchen von Verzeichnissen, die Dateiüberwachung usw.

Lese- und Schreiboperationen werden mit Hilfe der Stream-Klasse ausgeführt, mit der lediglich beschrieben wird, wie Bytes in eine Art Sicherungsspeicher gelesen und geschrieben werden können. Stream ist eine abstrakte Klasse, daher werden tatsächlich von Stream abgeleitete Klassen verwendet. Es sind folgende Klassen verfügbar:

Von Stream abgeleitete E/A-Klassen:

Klasse Beschreibung
FileStream Ein Stream einer Datenträgerdatei
MemoryStream Ein im Speicher gespeicherter Stream
NetworkStream Ein Stream für eine Netzwerkverbindung
BufferedStream Implementiert einen Puffer oberhalb eines anderen Streams

Mit Ausnahme von BufferedStream, der sich oberhalb eines anderen Streams befindet, wird durch jeden Stream der Bestimmungsort der Daten definiert.

Die Stream-Klasse stellt Funktionen zum Lesen und Schreiben auf Byteebene bereit, sowohl für synchrone als auch asynchrone Operationen. Es ist jedoch günstiger, oberhalb des Streams über eine Schnittstelle höherer Ebene zu verfügen. Hier stehen einige Schnittstellen zur Verfügung, die je nach gewünschtem Endformat ausgewählt werden können.


Galileo Computing

31.6.1 Binärklassen  downtop

Die Klassen BinaryReader und BinaryWriter werden zum Lesen und Schreiben von Werten im Binärformat (Rohformat) verwendet. Beispielsweise kann BinaryWriter zum Schreiben von int-Werten eingesetzt werden, gefolgt von float und einem weiteren int.

Diese Klassen werden in der Regel – keine Überraschung – zum Lesen und Schreiben binärer Formate eingesetzt und operieren auf Streamebene.


Galileo Computing

31.6.2 Textklassen  downtop

Die abstrakten Klassen TextReader und TextWriter definieren, wie Text gelesen und geschrieben wird. Sie ermöglichen Operationen für Zeichen, Zeilen, Blöcke usw. Es sind zwei verschiedene Implementierungen von TextReader verfügbar.

Die StreamWriter-Klasse wird für »normale« E/A-Operationen verwendet (Öffnen von Dateien, Auslesen von Zeilen) und operiert auf Streamebene (Stream).

Die Klassen StringReader und StringWriter können zum Lesen und Schreiben von Zeichenfolgen verwendet werden.


Galileo Computing

31.6.3 XML  downtop

Die Klassen XmlTextReader und XmlTextWriter werden für XML-Lese- und Schreiboperationen eingesetzt. Sie ähneln vom Entwurf her den Klassen TextReader und TextWriter, sind aber nicht von diesen Klassen abgeleitet, da sie keinen Text, sondern XML-Einheiten handhaben. Es handelt sich um Lowlevelklassen, die zum Erstellen oder Decodieren von XML verwendet werden.


Galileo Computing

31.6.4 Lese- und Schreibvorgänge für Dateien  downtop

Es gibt zwei Möglichkeiten, Streams mit Dateiverbindungen zu erhalten. Die erste Möglichkeit besteht darin, die FileStream-Klasse zu verwenden, die vollständige Steuerung über den Dateizugriff bietet, Zugriffsmodus, gemeinsame Verwendung und Puffer eingeschlossen.

using System;
using System.IO;

class Test
{
    public static void Main()
    {
        FileStream f = new FileStream("output.txt", FileMode.Create);
        StreamWriter s = new StreamWriter(f);

        s.WriteLine("{0} {1}", "test", 55);
        s.Close();
        f.Close();
    }
}

Statt dessen können auch die Funktionen der Klasse File dazu verwendet werden, einen Stream für eine Datei zu erhalten. Diese Methode ist besonders geeignet, wenn bereits ein File-Objekt mit den Dateiinformationen zur Verfügung steht, wie in der PrintFile()-Funktion im nächsten Beispiel.


Galileo Computing

31.6.5 Durchsuchen von Verzeichnissen  downtop

Dieses Beispiel zeigt, wie eine Verzeichnisstruktur durchsucht wird. Es wird eine DirectoryWalker-Klasse definiert, die für jedes Verzeichnis und jede Datei Zuweisungen sowie einen zu durchlaufenden Pfad enthält.

using System;
using System.IO;

public class DirectoryWalker
{
    public delegate void ProcessDirCallback(Directory dir, int level, 
object obj);
    public delegate void ProcessFileCallback(File file, int level, object 
obj);

    public DirectoryWalker(    ProcessDirCallback dirCallback,
                ProcessFileCallback fileCallback)
    {
        this.dirCallback = dirCallback;
        this.fileCallback = fileCallback;
    }

    public void Walk(string rootDir, object obj)
    {
        DoWalk(new Directory(rootDir), 0, obj);
    }
    void DoWalk(Directory dir, int level, object obj)
    {
        foreach (FileSystemEntry d in dir.GetFileSystemEntries ())
        {
            if (d is File)
            {
                if (fileCallback != null)
                    fileCallback((File) d, level, obj);
            }
            else
            {
                if (dirCallback != null)
                    dirCallback((Directory) d, level, obj);
                DoWalk((Directory) d, level + 1, obj);
            }
        }
    }

    ProcessDirCallback    dirCallback;
    ProcessFileCallback    fileCallback;
}

class Test
{
    public static void PrintDir(Directory d, int level, object obj)
    {
        WriteSpaces(level * 2);
        Console.WriteLine("Dir: {0}", d.FullName);
    }
    public static void PrintFile(File f, int level, object obj)
    {
        WriteSpaces(level * 2);
        Console.WriteLine("File: {0}", f.FullName);
    }
    public static void WriteSpaces(int spaces)
    {
        for (int i = 0; i < spaces; i++)
            Console.Write(" ");

    }
    public static void Main(string[] args)
    {
        DirectoryWalker dw = new DirectoryWalker(
            new DirectoryWalker.ProcessDirCallback(PrintDir),
            new DirectoryWalker.ProcessFileCallback(PrintFile));

        string root = ".";
        if (args.Length == 1)
            root = args[0];
        dw.Walk(root, "Passed string object");
    }
}

Galileo Computing

31.7 Serialisierung  downtop

Die Serialisierung ist der Vorgang, mit dem die Laufzeitumgebung Objekte in einer Art Speicher speichert oder von einem Standort an einen anderen überträgt.

Die Metadaten eines Objekts enthalten genügend Informationen, um der Laufzeitumgebung die Serialisierung der Felder zu ermöglichen, hierbei benötigt die Laufzeitumgebung jedoch ein wenig Unterstützung.

Diese Hilfe wird über zwei Attribute bereitgestellt. Über das Attribut [Serializable] wird gekennzeichnet, dass ein Objekt serialisiert werden kann. Das Attribut [NonSerialized] kann auf Felder oder Eigenschaften angewendet werden, die nicht serialisiert werden sollten. Dies ist sinnvoll, wenn es sich um einen Cachewert oder einen abgeleiteten Wert handelt.

Im folgenden Beispiel liegt eine Containerklasse namens MyRow vor, die Elemente der Klasse MyElements beinhaltet. Das Feld cacheValue in MyElement ist mit dem Attribut [NonSerialized] gekennzeichnet, damit es nicht serialisiert wird.

Im Beispiel wird das MyRow-Objekt serialisiert, in ein binäres Format gebracht und anschließend in ein XML-Format umgewandelt.

// Datei: serial.cs
// Kompilieren mit: csc serial.cs /r:system.runtime.serialization.formatters.soap.dll
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;

[Serializable]
public class MyElement
{
    public MyElement(string name)
    {
        this.name = name;
        this.cacheValue = 15;
    }
    public override string ToString()
    {
        return(String.Format("{0}: {1}", name, cacheValue));
    }
    string name;
        // Dieses Feld wird nicht beibehalten
    [NonSerialized]
    int cacheValue;
}
[Serializable]
public class MyRow
{
    public void Add(MyElement my)
    {
        row.Add(my);
    }

    public override string ToString()
    {
        string temp = null;
        foreach (MyElement my in row)
            temp += my.ToString() + "\n";
        return(temp);
    }

    ArrayList row = new ArrayList();
}

class Test
{
    public static void Main()
    {
        MyRow row = new MyRow();
        row.Add(new MyElement("Gumby"));
        row.Add(new MyElement("Pokey"));

        Console.WriteLine("Initial value");
        Console.WriteLine("{0}", row);

            // Umschreiben in binären Wert, erneut einlesen
        Stream streamWrite = File.Create("MyRow.bin");
        BinaryFormatter binaryWrite = new BinaryFormatter();
        binaryWrite.Serialize(streamWrite, row);
        streamWrite.Close();

        Stream streamRead = File.OpenRead("MyRow.bin");
        BinaryFormatter binaryRead = new BinaryFormatter();
        MyRow rowBinary = (MyRow) binaryRead.Deserialize(streamRead);
        streamRead.Close();

        Console.WriteLine("Values after binary serialization");
        Console.WriteLine("{0}", rowBinary);

            // Umschreiben in SOAP (XML), erneut einlesen
        streamWrite = File.Create("MyRow.xml");
        SoapFormatter soapWrite = new SoapFormatter();
        soapWrite.Serialize(streamWrite, row);
        streamWrite.Close();

        streamRead = File.OpenRead("MyRow.xml");
        SoapFormatter soapRead = new SoapFormatter();
        MyRow rowSoap = (MyRow) soapRead.Deserialize(streamRead);
        streamRead.Close();

        Console.WriteLine("Values after SOAP serialization");
        Console.WriteLine("{0}", rowSoap);
    }
}

Dieser Code erzeugt die folgende Ausgabe:

Initial value
Gumby: 15
Pokey: 15

Values after binary serialization
Gumby: 0
Pokey: 0

Values after SOAP serialization
Gumby: 0
Pokey: 0

Das Feld cacheValue wurde nicht beibehalten, da es mit [NonSerialized] gekennzeichnet war. Die Datei MyRow.Bin enthält die binäre Serialisierung, die Datei MyRow.xmlb enthält die XML-Version.

Die XML-Codierung ist eine SOAP-Verschlüsselung (Symbolic Optimizer Assembly Program). Zum Erzeugen einer spezifischen XML-Codierung kann die XmlSerializer-Klasse verwendet werden.


Galileo Computing

31.8 Threading  downtop

Der System.Threading-Namespace enthält nützliche Klassen für das Threading und die Synchronisierung. Der geeignete Typ für Synchronisierung und/oder Ausschluss richtet sich nach dem Programmentwurf, C# unterstützt jedoch den einfachen Ausschluss über die lock-Anweisung.

Lock verwendet die System.Threading.Monitor-Klasse und bietet ähnliche Funktionalität für CriticalSection-Aufrufe in Win32.

Im folgenden Beispiel wird die Erhöhung eines Kontensaldos simuliert. Der Code, mit dem die Saldoerhöhung erfolgt, ruft zunächst den aktuellen Kontostand in einer temporären Variablen ab und verweilt für eine Millisekunde. Die Wahrscheinlichkeit, dass während dieser Ruhephase – also bevor der erste Thread reaktiviert wird und den neuen Wert speichert – ein anderer Thread den Saldo abruft, ist sehr hoch.

Falls der Code wie geschrieben ausgeführt wird, liegt der Endwert unter dem erwarteten Ergebnis von 1.000. Durch Entfernen der Kommentare zur lock-Anweisung in der Deposit()-Funktion stellt das System sicher, dass sich im lock-Block immer nur ein Thread befindet, und das Endergebnis ist korrekt.

Das an die lock-Anweisung übergebene Objekt muss vom Verweistyp sein und sollte den zu schützenden Wert enthalten. Das Sperren der aktuellen Instanz mit this verhindert einen Zugriff durch die gleiche Instanz.

using System;
using System.Threading;

public class Account
{
    public Account(decimal balance)
    {
        this.balance = balance;
    }

    public void Deposit(decimal amount)
    {
        //lock(this)    // Auskommentieren, um Block zu schützen
        {
            Decimal temp = balance;
            temp += amount;
            Thread.Sleep(1);    // vorgesehene Ruhephase
            balance = temp;
        }
    }
    public Decimal Balance
    {
        get
        {
            return(balance);
        }
    }
    decimal balance;
}

class ThreadTest
{
    public void MakeDeposit()
    {
        for (int i = 0; i < 10; i++)
            account.Deposit(10);
    }

    public static void Main(string[] args)
    {
        ThreadTest b = new ThreadTest();
        Thread t = null;
            // 10 Threads erstellen
        for (int threads = 0; threads < 10; threads++)
        {
            t = new Thread(new ThreadStart(b.MakeDeposit));
            t.Start();
        }
        t.Join();    // Warten, bis die letzten drei Threads abgeschlossen 
sind
        Console.WriteLine("Balance: {0}", b.account.Balance);
    }
    Account    account = new Account(0);
}

Galileo Computing

31.9 Lesen von Webseiten  toptop

Das folgende Beispiel veranschaulicht, wie unter Verwendung von C# ein so genannter »Screenscraper« geschrieben wird. Der folgende Code enthält ein Aktiensymbol, formatiert eine URL-Adresse zum Abrufen eines Angebots von der Microsoft MoneyCentral-Site und extrahiert anschließend das Angebot mit Hilfe eines regulären Ausdrucks aus der HTML-Seite.

// Datei: quote.cs
// Kompilieren mit: csc quote.cs /r:system.net.dll /r:system.text.regularexpressions.dll
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

class QuoteFetch
{
    public QuoteFetch(string symbol)
    {
        this.symbol = symbol;
    }

    public string Last
    {
        get
        {
            string url = "http://moneycentral.msn.com/scripts/
            webquote.dll?ipage=qd&Symbol=";
            url += symbol;

            ExtractQuote(ReadUrl(url));
            return(last);
        }
    }
    string ReadUrl(string url)
    {
        URI uri = new URI(url);

            // Angefordertes Objekt erstellen

        WebRequest req = WebRequestFactory.Create(uri);
        WebResponse resp = req.GetResponse();
        Stream stream = resp.GetResponseStream();
        StreamReader sr = new StreamReader(stream);

        string s = sr.ReadToEnd();

        return(s);

    }
    void ExtractQuote(string s)
    {
        // Zeile wie: "Last</TD><TD ALIGN=RIGHT NOWRAP><B>&nbsp;78 
3/16"?

        Regex lastmatch = new Regex(@"Last\D+(?<last>.+)<\/B>");
        last = lastmatch.Match(s).Group(1).ToString();
    }
    string    symbol;
    string    last;
}

class Test
{
    public static void Main(string[] args)
    {
        if (args.Length != 1)
            Console.WriteLine("Quote <symbol>");
        else
        {
            QuoteFetch q = new QuoteFetch(args[0]);
            Console.WriteLine("{0} = {1}", args[0], q.Last);
        }
    }
}
   

Einstieg in Visual C# 2005

Visual C#

Einstieg in Visual Basic .NET 2005

Visual Basic .NET 2005






Copyright © Galileo Press GmbH 2001 - 2002
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: 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.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, fon: 0228.42150.0, fax 0228.42150.77, info@galileo-press.de