Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort zur 6. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Das Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Strukturen und Enumerationen
7 Fehlerbehandlung und Debugging
8 Auflistungsklassen (Collections)
9 Generics – Generische Datentypen
10 Weitere C#-Sprachfeatures
11 LINQ
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 XML
15 Multithreading und die Task Parallel Library (TPL)
16 Einige wichtige .NET-Klassen
17 Projektmanagement und Visual Studio 2012
18 Einführung in die WPF und XAML
19 WPF-Layout-Container
20 Fenster in der WPF
21 WPF-Steuerelemente
22 Elementbindungen
23 Konzepte von WPF
24 Datenbindung
25 Weitere Möglichkeiten der Datenbindung
26 Dependency Properties
27 Ereignisse in der WPF
28 WPF-Commands
29 Benutzerdefinierte Controls
30 2D-Grafik
31 ADO.NET – Verbindungsorientierte Objekte
32 ADO.NET – Das Command-Objekt
33 ADO.NET – Der SqlDataAdapter
34 ADO.NET – Daten im lokalen Speicher
35 ADO.NET – Aktualisieren der Datenbank
36 Stark typisierte DataSets
37 Einführung in das ADO.NET Entity Framework
38 Datenabfragen des Entity Data Models (EDM)
39 Entitätsaktualisierung und Zustandsverwaltung
40 Konflikte behandeln
41 Plain Old CLR Objects (POCOs)
Stichwort

Download:
- Beispiele, ca. 62,4 MB

Buch bestellen
Ihre Meinung?

Spacer
Visual C# 2012 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2012

Visual C# 2012
Rheinwerk Computing
1402 S., 6., aktualisierte und erweiterte Auflage 2013, geb., mit DVD
49,90 Euro, ISBN 978-3-8362-1997-6
Pfeil 13 Binäre Serialisierung
Pfeil 13.1 Einführung in die Serialisierung
Pfeil 13.1.1 Serialisierungsverfahren
Pfeil 13.2 Serialisierung mit »BinaryFormatter«
Pfeil 13.2.1 Die Deserialisierung
Pfeil 13.2.2 Serialisierung mehrerer Objekte

Galileo Computing - Zum Seitenanfang

13.2 Serialisierung mit »BinaryFormatter«Zur nächsten Überschrift

Ein Objekt unter .NET binär serialisieren zu können, ist genial einfach gelöst. Allerdings muss man sich schon bei der Entwicklung einer Klasse darüber klar sein, dass Objekte der Klasse dem Serialisierungsprozess zugeführt werden sollen. Dazu wird die Klasse nur mit dem Attribut Serializable markiert:

[Serializable()] 
public class Person {

[...]
}

Listing 13.1 Attribut »Serializable«, um eine Klasse binär zu serialisieren

Fehlt das Attribut, wird die Ausnahme SerializationException ausgelöst. Alle Felder der Klasse Person, unabhängig davon, ob sie privat oder öffentlich deklariert sind, werden damit von der Serialisierung erfasst. Es gibt aber auch eine Einschränkung: Lokale Variablen und statische Klassendaten nehmen nicht an einem Serialisierungsprozess teil.

Wir wollen nun die Klassendefinition komplettieren, um anhand eines einfachen Beispiels zu sehen, wie die Serialisierung angestoßen und später das serialisierte Objekt rekonstruiert wird. Dazu implementieren wir in der Klasse Person ein privates und ein öffentliches Feld. Beide Felder werden über einen parametrisierten Konstruktor initialisiert.

[Serializable()] 
class Person
{
public string Name {get; set;}
private int _Alter;

// ----- Konstruktor -----

public Person(int alter, string name) {
_Alter = alter;
Name = name;
}

public int Alter {
get { return _Alter; }
}
}

Bei der Serialisierung greift der Prozess den Inhalt von Alter und Name und speichert ihn entweder in einer Datei, im Netzwerk oder in einer Datenbank.

Der Code, der ein Objekt vom Typ der Klasse Person serialisiert, könnte folgendermaßen aussehen:

using System.Runtime.Serialization.Formatters.Binary;

class Program {

static void Main(string[] args) {

[...]

Person pers = new Person(56, "Schmidt");
FileStream stream;
stream = new FileStream(@"D:\MyPerson.dat", FileMode.Create);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, pers);
stream.Close();
[...]
}
}

Listing 13.2 Serialisierung eines Objekts

Im Code wird ein FileStream generiert, der die binäre Datei MyPerson.dat anlegt oder, falls eine Datei dieses Namens bereits existiert, die alte überschreibt. Anschließend erzeugen wir ein Objekt vom Typ BinaryFormatter, dessen Methode Serialize wir aufrufen. Dabei übergeben wir das Stream-Objekt und die zu serialisierende Objektreferenz.

Felder als nichtserialisierbar kennzeichnen

Mit dem Attribut Serializable werden alle Felder einer Klasse, sowohl public als auch private definierte, serialisiert. Das mag im Einzelfall aber nicht immer wünschenswert sein. Eigenschaften, die der Serialisierungsprozess nicht erfassen soll, können durch das Setzen des Attributs NonSerialized vor der Deklaration ausgeschlossen werden.

[Serializable()] 
class Person
{
public string Name {get; set;}

// das Feld Alter wird nicht serialisiert

[NonSerialized()]
private int _Alter;
[...]
}

Das Codefragment enthält die Klassendefinition der Felder Name und Alter. Beide würden normalerweise während der Serialisierung abgegriffen. Die Deklaration der privaten Variablen Alter ist allerdings als NonSerialized markiert und entzieht das Feld dem Serialisierungsprozess.

Serialisierung in einer abgeleiteten Klasse

Das Serializable-Attribut wird nicht vererbt. Wenn Sie also beispielsweise unserer Klasse Circle das Serializable-Attribut spendieren, erbt die abgeleitete Klasse GraphicCircle das Attribut nicht. Soll auch ein Objekt vom Typ GraphicCircle serialisierbar sein, muss diese Klasse ebenfalls mit Serializable verknüpft werden. Ansonsten gilt die abgeleitete Klasse als nicht serialisierbar.


Galileo Computing - Zum Seitenanfang

13.2.1 Die DeserialisierungZur nächsten ÜberschriftZur vorigen Überschrift

Die Deserialisierung des gespeicherten Objekts ist genauso einfach. Beachtet werden muss dabei nur, dass der Rückgabewert vom Typ Object ist und deshalb noch in den richtigen Typ konvertiert werden muss:

[...]
Person pers;
BinaryFormatter formatter = new BinaryFormatter();
FileStream stream = new FileStream(@"D:\MyPerson.dat", FileMode.Open);
pers = (Person)formatter.Deserialize(stream);
stream.Close();
[...]

Listing 13.3 Deserialisierung eines Objekts

Fassen wir nun den Code in einem Beispielprogramm zusammen. Serialisierung und Deserialisierung werden in je einer eigenen Methode behandelt, die aus Main heraus aufgerufen wird. Nach der Serialisierung des Objekts pers wird der neuen Objektvariablen oldPerson der Rückgabewert der Deserialisierung zugewiesen. Zum Schluss werden die rekonstruierten Objektdaten an der Konsole ausgegeben.

Beispiel: ..\Kapitel 13\BinaryFormatterSample

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

class Program {
static BinaryFormatter formatter;
static FileStream stream;

static void Main(string[] args) {
formatter = new BinaryFormatter();
Person pers = new Person(67, "Fischer");
SerializeObject(pers);
Person oldPerson = DeserializeObject();
Console.WriteLine("Ergebnis der Deserialisierung:");
Console.WriteLine(oldPerson.Alter);
Console.WriteLine(oldPerson.Name);
}

// Objekt serialisieren

public static void SerializeObject(Object obj) {
stream = new FileStream(@"D:\MyObject.dat", FileMode.Create);
formatter.Serialize(stream, obj);
stream.Close();
}

// Objekt deserialisieren

public static Person DeserializeObject() {
FileStream stream = new FileStream(@"D:\MyObject.dat", FileMode.Open);
return (Person)formatter.Deserialize(stream);
}
}

// binär serialisierbare Klasse

[Serializable()]
class Person
{
private int _Alter;
public string Name {get; set;}

// Konstruktor

public Person(int alter, string name) {
Name = name;
_Alter = alter;
}

public int Alter {
get { return _Alter; }
}
}

Listing 13.4 Beispielprogramm zur binären Serialisierung


Galileo Computing - Zum Seitenanfang

13.2.2 Serialisierung mehrerer ObjekteZur vorigen Überschrift

Natürlich können mit einem Serialisierungsprozess auch beliebig viele, auch typunterschiedliche Objekte serialisiert werden. Dazu muss man für jedes Objekt Serialize auf demselben Stream-Objekt aufrufen. Die formatierten Daten werden entsprechend der Aufrufreihenfolge serialisiert.

Die Deserialisierung erfolgt in gleicher Weise: Es wird auf demselben Stream-Objekt so lange Deserialize aufgerufen, bis der Datenstrom versiegt. Das Lesen über das Ende des Datenstroms hinaus hat eine Ausnahme zur Folge. Dabei muss natürlich die Reihenfolge beachtet werden, in der die Objekte serialisiert worden sind, denn die Deserialisierung mehrerer Objekte folgt dem FIFO-Prinzip: Das zuerst serialisierte Objekt muss auch als Erstes wieder deserialisiert werden.

Für jedes einzelne Objekt Serialize aufzurufen, kann sehr arbeitsaufwendig sein. Außerdem muss die Anzahl der zu serialisierenden Objekte bekannt sein. Ist die Anzahl nicht vorhersehbar, muss ein anderer Weg beschritten werden. Es bietet sich dann an, alle Objekte in einer Objektauflistung zu verwalten und mit einem einzigen Serialize-Aufruf die gesamte Collection in den Datenstrom zu schreiben. Im folgenden Beispielprogramm wird das an vier Objekten demonstriert.

Sehen wir uns jedoch zuerst den Programmcode an. Dazu werden einer Auflistung vom Typ List<GeometricObject> insgesamt vier Circle- und Rectangle-Objekte hinzugeführt. Anschließend wird die Liste serialisiert und zur Probe auch deserialisiert.

Vorher müssen die Klassen GeometricObject, Circle, GraphicCircle, Rectangle, GraphicRectangle und natürlich auch die Struktur Point mit dem Serializable-Attribut verknüpft werden. Die Klasse List<T> hat natürlich dieses Attribut.

// Beispiel: ...\Kapitel 13\GeometricObjectsSolution_12

class Program {

static void Main(string[] args) {
List<GeometricObject> liste = new List<GeometricObject>();
liste.Add(new Circle(100, -50, 75));
liste.Add(new Rectangle(120, 46, 310, 210));
liste.Add(new Circle(69, 70, -200));
liste.Add(new Rectangle(58, 45, -10, -20));

// Liste serialisieren

SaveList(liste);

// Liste deserialisieren

List<GeometricObject> newList = GetListObjects();

foreach (var item in newList)
{
Circle circle = item as Circle;
if (circle != null)
Console.WriteLine("Circle: Radius = {0,-5}X={1,-5}Y={2}",
circle.Radius, circle.XCoordinate, circle.XCoordinate);
else
{
Rectangle rect = item as Rectangle;
Console.WriteLine("Rectangle: Length ={0,-5}Width={1,-5}
X={2,-5}Y={3}", rect.Length, rect.Width,
rect.XCoordinate, rect.XCoordinate);
}
}
Console.ReadLine();
}

public static void SaveList(IList<GeometricObject> elements) {
FileStream stream = new FileStream(@"D:\GeoObjects.dat",
FileMode.Create);
BinaryFormatter binFormatter = new BinaryFormatter();
binFormatter.Serialize(stream, elements);
stream.Close();
}

public static List<GeometricObject> GetListObjects() {
FileStream stream = new FileStream(@"D:\GeoObjects.dat", FileMode.Open);
List<GeometricObject> oldList = null;
try
{
BinaryFormatter formatter = new BinaryFormatter();
oldList = (List<GeometricObject>)formatter.Deserialize(stream);
}
catch (SerializationException e)
{

// die Datei kann nicht serialisiert werden

Console.WriteLine(e.Message);
}
catch (IOException e)
{

// Beim Versuch, die Datei zu öffnen, ist ein Fehler aufgetreten

Console.WriteLine(e.Message);
}
return oldList;
}
}

Listing 13.5 Serialisierung geometrischer Objekte

Die Entscheidung für eine Auflistung hat einen entscheidenden Vorteil: Wir brauchen nicht jedes Mitgliedsobjekt der Auflistung einzeln zu serialisieren, sondern können mit einem einzigen Aufruf von Serialize unter Übergabe der List<T>-Referenz automatisch jedes Objekt in den Datenstrom schreiben. Das geschieht in der Methode SaveList, der im Parameter elements die Referenz auf ein Objekt vom Typ List<GeometricObject> übergeben wird.

In der Methode GetListObjects ist nur ein Deserialize-Aufruf notwendig, um die Daten aller von der Collection verwalteten Objekte wiederzuerhalten. Der Rückgabewert vom Typ List<GeometricObject> liefert die vollständig deserialisierte Liste als Referenz an den Aufrufer zurück.

Im Hauptprogramm wird nach der Deserialisierung die von der Methode GetListObjects zurückgegebene Liste in einer foreach-Schleife durchlaufen. Um auch die typspezifischen Member Radius, Length und Width abrufen zu können, ist eine Typkonvertierung notwendig. Im Beispiel wird das mit dem as-Operator durchgeführt.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual C# 2012

Visual C# 2012
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: C++ Handbuch






 C++ Handbuch


Zum Katalog: C/C++






 C/C++


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo





Copyright © Rheinwerk Verlag GmbH 2013
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.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de