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

Inhaltsverzeichnis
Vorwort zur 5. Auflage
1 Allgemeine Einführung in .NET
2 Grundlagen der Sprache C#
3 Klassendesign
4 Vererbung, Polymorphie und Interfaces
5 Delegates und Ereignisse
6 Weitere .NET-Datentypen
7 Weitere Möglichkeiten von C#
8 Auflistungsklassen (Collections)
9 Fehlerbehandlung und Debugging
10 LINQ to Objects
11 Multithreading und die Task Parallel Library (TPL)
12 Arbeiten mit Dateien und Streams
13 Binäre Serialisierung
14 Einige wichtige .NET-Klassen
15 Projektmanagement und Visual Studio 2010
16 XML
17 WPF – Die Grundlagen
18 WPF-Containerelemente
19 WPF-Steuerelemente
20 Konzepte der WPF
21 Datenbindung
22 2D-Grafik
23 ADO.NET – verbindungsorientierte Objekte
24 ADO.NET – Das Command-Objekt
25 ADO.NET – Der SqlDataAdapter
26 ADO.NET – Daten im lokalen Speicher
27 ADO.NET – Aktualisieren der Datenbank
28 Stark typisierte DataSets
29 LINQ to SQL
30 Weitergabe von Anwendungen
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual C# 2010 von Andreas Kühnel
Das umfassende Handbuch
Buch: Visual C# 2010

Visual C# 2010
geb., mit DVD
1295 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1552-7
Pfeil 4 Vererbung, Polymorphie und Interfaces
Pfeil 4.1 Die Vererbung
Pfeil 4.1.1 Basisklassen und abgeleitete Klassen
Pfeil 4.1.2 Ableitung einer Klasse
Pfeil 4.1.3 Klassen, die nicht abgeleitet werden können
Pfeil 4.1.4 Konstruktoren in abgeleiteten Klassen
Pfeil 4.1.5 Der Zugriffsmodifizierer »protected«
Pfeil 4.1.6 Konstruktorverkettung in der Vererbung
Pfeil 4.2 Der Problemfall geerbter Methoden
Pfeil 4.2.1 Geerbte Methoden mit »new« verdecken
Pfeil 4.2.2 Abstrakte Methoden
Pfeil 4.2.3 Virtuelle Methoden
Pfeil 4.3 Typumwandlung und Typuntersuchung von Objektvariablen
Pfeil 4.3.1 Implizite Typumwandlung von Objektreferenzen
Pfeil 4.3.2 Explizite Typumwandlung von Objektreferenzen
Pfeil 4.3.3 Typuntersuchung mit dem is-Operator
Pfeil 4.3.4 Typumwandlung mit dem as-Operator
Pfeil 4.4 Polymorphie
Pfeil 4.4.1 »Klassische« Methodenimplementierung
Pfeil 4.4.2 Abstrakte Methoden
Pfeil 4.4.3 Virtuelle Methoden
Pfeil 4.4.4 Versiegelte Methoden
Pfeil 4.4.5 Überladen einer Basisklassenmethode
Pfeil 4.4.6 Statische Member und Vererbung
Pfeil 4.5 Das Projekt »GeometricObjectsSolution« ergänzen
Pfeil 4.6 Hat-ein(e)-Beziehungen
Pfeil 4.6.1 Innere Klassen (Nested Classes)
Pfeil 4.7 Interfaces (Schnittstellen)
Pfeil 4.7.1 Schnittstellendefinition
Pfeil 4.7.2 Schnittstellenimplementierung
Pfeil 4.7.3 Interpretation der Schnittstellen
Pfeil 4.8 Das Zerstören von Objekten – der Garbage Collector
Pfeil 4.8.1 Arbeitsweise des Garbage Collectors
Pfeil 4.8.2 Expliziter Aufruf des Garbage Collectors
Pfeil 4.8.3 Der Destruktor
Pfeil 4.8.4 Die »IDisposable«-Schnittstelle
Pfeil 4.8.5 Ergänzungen in den Klassen »Circle« und »Rectangle«


Galileo Computing - Zum Seitenanfang

4.6 Hat-ein(e)-Beziehungen Zur nächsten ÜberschriftZur vorigen Überschrift

Eine Ist-ein(e)-Beziehung wird durch eine Vererbungsbeziehung geprägt. Ein Objekt vom Typ einer abgeleiteten Klasse ist gleichzeitig auch immer vom Typ der Basisklasse. Die daraus resultierenden Konsequenzen haben wir in den letzten Abschnitten ausgiebig diskutiert. Die ziemlich abstrakt anmutende Denkweise der Vererbung lässt sich aber recht gut auf die reale Welt abbilden. Allerdings können wir damit nicht alle Forderungen abdecken. Stellen Sie sich beispielsweise nur vor, Sie müssten die Klasse eines Motorflugzeugs modellieren. Die Klasse Flugzeug könnte die Zustandsdaten bereitstellen (beispielsweise die Spannweite), die in einem Konstruktor initialisiert werden, z. B. so:


public class Flugzeug {
  private double spannweite;
  public Flugzeug(double Spannweite) {
    spannweite = Spannweite;
  }
  // weiterer Klassencode
}

Da es sich um die Beschreibung eines Motorflugzeugs handelt, sollten wir auch noch die Klasse Triebwerk bereitstellen. Die Daten eines Triebwerks werden im Konstruktor initialisiert, außerdem enthält der Entwurf zwei Methoden, mit denen das Triebwerk angelassen und abgeschaltet werden kann.


public class Triebwerk {
  private int leistung;
  private double gewicht;
  public Triebwerk(int leistung, double gewicht) {
    this.leistung = leistung;
    this.gewicht = gewicht;
  }
  // Triebwerk starten
  public void StartEngine() {
    Console.WriteLine("Das Triebwerk wird gestartet");
  }
  // Triebwerk ausschalten
  public void StopEngine() {
    Console.WriteLine("Das Triebwerk wird abgeschaltet");
  }
}

In der realen Welt stehen Objekte dieser beiden Klassen miteinander in einer Beziehung, die nicht durch eine Vererbungslinie abgebildet werden kann. Denken Sie daran, dass die Implementierungsvererbung eine Beziehung zwischen Typen beschreibt, die als Ist-ein(e)-Beziehung bezeichnet wird. Dass ein Flugzeug aber kein Triebwerk ist oder umgekehrt ein Triebwerk kein Flugzeug, leuchtet ein.

Um die beiden miteinander in einer unzertrennlichen Beziehung stehenden Typen Flugzeug und Triebwerk zu beschreiben, müsste man sagen, dass ein Flugzeug ein Triebwerk hat. Der vorliegende Sachverhalt einer Hat-ein(e)-Beziehung wird auch im objektorientierten Sprachgebrauch zum Ausdruck gebracht. Ein Flugzeug hat ein Triebwerk, folgerichtig muss die Klassendefinition Flugzeug eine Eigenschaft vom Typ Triebwerk haben. Die Klasse Flugzeug kann mit dieser Überlegung folgendermaßen verbessert werden:


public class Flugzeug {
  private Triebwerk motor;
  ...
}

In der übergeordneten Klasse Flugzeug ist das untergeordnete Objekt vom Typ Triebwerk als private deklariert. Diese Kapselung entspricht den Paradigmen der objektorientierten Programmierung, wirft jedoch auch sofort die Frage auf, wie ein Objekt vom Typ Triebwerk instanziiert und später gestartet bzw. ausgeschaltet werden kann.

Um bei der Instanziierung eines Flugzeugobjekts sofort eine gültige Referenz auf ein Triebwerksobjekt zu erhalten, eignet sich der Konstruktor der Klasse Flugzeug, in dem die Klasse Triebwerk instanziiert und mit den notwendigen Initialisierungsdaten versorgt wird:


public class Flugzeug {
  private Triebwerk motor;
  ...
  // aktualisierter Konstruktor der Klasse Flugzeug
  public Flugzeug(double spannweite, int leistung,
                  double gewicht) {
    this.spannweite = spannweite;
    motor = new Triebwerk(leistung, gewicht);
  }
  // weiterer Klassencode
}

Nun beschreibt ein Objekt vom Typ Flugzeug ein ganz bestimmtes Objekt vom Typ Triebwerk – das Triebwerk ist dem Flugzeug eindeutig zugeordnet. Was bleibt, ist das Starten und Ausschalten des Triebwerks. Dazu bieten sich zwei Alternativen an:

  • Die Definition einer Eigenschaft, die die Referenz auf das Triebwerk zurückliefert. Auf dieser Referenz können anschließend die triebwerksspezifischen Methoden StartEngine und StopEngine aufgerufen werden.
  • Die Definition einer Methode in der Klasse Flugzeug, die den Methodenaufruf an das untergeordnete Triebwerksobjekt weiterleitet.

Weiterleitung einer internen Objektreferenz

Sehen wir uns zuerst die Realisierung mittels einer Eigenschaftsmethode an, die die Referenz auf das Triebwerksobjekt an den Aufrufer weiterleitet. Auf den set-Accessor kann verzichtet werden, weil das Triebwerksobjekt bereits im Konstruktor erzeugt wird:


public class Flugzeug {
  private Triebwerk motor;
  public Triebwerk Motor {
    get {return motor;}
  }
  // weiterer Klassencode
}

Sehen wir uns den Programmcode zusammengefasst an:


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 4\HatEineBeziehung_1 
// ------------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    Flugzeug myCessna = new Flugzeug(8, 150, 300);
    myCessna.Motor.StartEngine();
    myCessna.Motor.StopEngine();
    Console.ReadLine();
  }
}
public class Flugzeug {
  private double spannweite;
  private Triebwerk motor;
  public Flugzeug(double spannweite, int leistung, double gewicht) {
    this.spannweite = spannweite;
    motor = new Triebwerk(leistung, gewicht);
  }
  public Triebwerk Motor {
    get {return motor;}
  }
}
public class Triebwerk {
  private int leistung;
  private double gewicht;
  public Triebwerk(int leistung, double gewicht) {
    this.leistung = leistung;
    this.gewicht = gewicht;
  }
  public void StartEngine() {
    Console.WriteLine("Das Triebwerk wird gestartet");
  }
  public void StopEngine() {
    Console.WriteLine("Das Triebwerk wird abgeschaltet");
  }
}

Der Benutzer besorgt sich nach der Instanziierung der Klasse Flugzeug die Referenz auf das interne Triebwerksobjekt und kann darauf die beiden Methoden StartEngine und StopEngine aufrufen:


Flugzeug myCessna = new Flugzeug(8, 150, 300);
myCessna.Motor.StartEngine();
...
myCessna.Motor.StopEngine();

Der Teilausdruck


myCessna.Motor

liefert die Referenz auf das Triebwerk, über die mit der üblichen Punktnotation auf alle Klassenmitglieder des internen Objekts zugegriffen werden kann. Festzustellen ist, dass dem Aufrufer nicht verborgen bleibt, dass ein zweites, internes Objekt der übergeordneten Klasse seine Dienste bereitstellt.

Verbergen des internen Objekts

Betrachten wir nun die zweite Alternative. In der Klasse Flugzeug sind dazu zwei Methoden definiert: die erste, um das Triebwerk einzuschalten, und die zweite, um es wieder auszuschalten. Der Aufruf wird in den Methoden an das interne Triebwerksobjekt weitergeleitet.


public class Flugzeug {
  private Triebwerk motor;
  ...
  public void StartMotor() {
    motor.StartEngine();
  }
  public void StopMotor() {
    motor.StopEngine();
  }
}

Der Aufruf einer der beiden Instanzmethoden StartMotor oder StopMotor des Flugzeug-Objekts bewirkt, dass das Triebwerk das gewünschte Verhalten zeigt: Es wird angelassen oder abgeschaltet. Anders als bei der Weiterleitung der internen Objektreferenz bemerkt der Aufrufer nicht, dass das Flugzeug-Objekt sich hinterlistig der Funktionalität einer zweiten Klasse bedient. Wenn wir voraussetzen, dass die Codeimplementierung der beiden Methoden in der untergeordneten Klasse Triebwerk möglicherweise sehr komplex ist, gaukelt diese Lösung dem Aufrufer Intelligenz vor und verbirgt dabei den tatsächlichen Dienstanbieter – ganz im Gegensatz zu der zuerst vorgestellten Variante.

Fassen wir an dieser Stelle den Programmcode wieder zusammen:


// ------------------------------------------------------------------
// Beispiel: ...\Kapitel 4\HatEineBeziehung_2
// ------------------------------------------------------------------
class Program {
  static void Main(string[] args) {
    Flugzeug myPiper = new Flugzeug(9, 180, 250);
    myPiper.StartMotor();
    myPiper.StopMotor();
    Console.ReadLine();
  }
}
// ----------- Klasse Flugzeug -----------
class Flugzeug {
  private double spannweite;
  private Triebwerk motor;  
  public Flugzeug(double Spannweite, int Leistung,
                  double Gewicht) {
    spannweite = Spannweite;
    motor = new Triebwerk(Leistung, Gewicht);
  }
  public void StartMotor() {
    motor.StartEngine();
  }
  public void StopMotor() {
    motor.StopEngine();
  }
}
// ---------- Klasse Triebwerk -----------
public class Triebwerk {
  private int leistung;
  private double gewicht;
  public Triebwerk(int Leistung, double Gewicht) {
    leistung = Leistung;
    gewicht = Gewicht;
  }
  public void StartEngine() {
    Console.WriteLine("Der Motor wird gestartet");
  }
  public void StopEngine() {
    Console.WriteLine("Der Motor wird abgeschaltet");
  }
}


Galileo Computing - Zum Seitenanfang

4.6.1 Innere Klassen (Nested Classes) topZur vorigen Überschrift

Es gibt noch eine andere Möglichkeit, Hat-ein(e)-Beziehungen zu realisieren. Dazu wird im Gültigkeitsbereich einer Klasse eine weitere Klasse definiert:


public class ClassA {
  public class ClassB {
    // Code der Klasse ClassB
  }
  // Code der Klasse ClassA
}

Eine Klasse, die innerhalb einer anderen Klasse definiert ist, wird als innere Klasse bezeichnet. Im Codefragment ist das die Definition der Klasse ClassB in ClassA. Die Verwendung innerer Klassen ist eine bequeme Möglichkeit zur Bereitstellung untergeordneter Objekte und wird benutzt, wenn ein Typ einem anderen Typ logisch zugeordnet werden kann.

Rufen Sie sich noch einmal das Beispiel des Flugzeugs und des Triebwerks in Erinnerung, deren Klassen wir weiter oben folgendermaßen definiert hatten:


class Flugzeug {/*...*/}
class Triebwerk {/*...*/}

Ein Triebwerk ist eine Bauteilkomponente eines Flugzeugs. Wenn kein Zwang dazu besteht, ein Triebwerk auch als separates Objekt einer Betrachtung zu unterziehen, wäre es überlegenswert, die Definition Triebwerk in der Definition Flugzeug einzuschließen:


public class Flugzeug {
  public class Triebwerk {
    ...
  }
}

Ein Objekt vom Typ Triebwerk ist jetzt abhängig von einem Objekt des Typs Flugzeug und kann nur im Kontext der äußeren Klasse, hier also von Flugzeug, benutzt werden.

Ist die innere Klasse als public deklariert, muss bei der Instanziierung immer der Typ der äußeren Klasse angegeben werden.


Flugzeug.Triebwerk turbine = new Flugzeug.Triebwerk(545, 320);

Die eindeutige Zugehörigkeit eines Triebwerk-Objekts zu einem Flugzeug zeigt sich erst, wenn das Flugzeug-Objekt ein ihm zugeordnetes Triebwerk hat. Dazu wird im Konstruktor der äußeren Klasse die innere Klasse instanziiert und die Referenz in einer gekapselten Variablen vorgehalten.


public class Flugzeug {
  private Triebwerk turbine;
  public Flugzeug() {
    turbine = new Triebwerk();
  }
  public class Triebwerk {/*...*/}
}

Festhalten können wir, dass sich im Vergleich zu zwei separaten Klassendefinitionen, wie wir sie anfangs hatten, nicht viel geändert hat. Interessant wird es, wenn die innere Klasse als private deklariert wird:


public class Flugzeug {
  ...
  private class Triebwerk {/*...*/}
}

Die Klasse Triebwerk ist nun ein eindeutig einem Flugzeug-Objekt zugeordnetes Element. Kein anderer Code, außer dem Code in der äußeren Klasse, hat Zugriff auf den inneren Typ.

Doch wie kann ein Aufrufer ein Triebwerk starten, wenn der Typ Triebwerk für ihn völlig unsichtbar bleibt und er deshalb nicht die klassenspezifischen Methoden aufrufen kann? Die Lösung führt wieder über Methoden, die in der äußeren Klasse Flugzeug definiert sind:


public class Flugzeug {
  private Triebwerk motor = new Triebwerk(200, 56);
  ...
  public void TurbineStarten() {
    motor.StartEngine();
  }
  // innere Klasse
  private class Triebwerk {
    ...
     public void StartEngine() {
       Console.WriteLine("Die Turbine wird gestartet");
     }
  }
}

Weil die Klasse Triebwerk jetzt als private deklariert ist, hat sie eine Sichtbarkeit wie jede andere private Entität der äußeren Flugzeug-Klasse.

Ein Flugzeug-Objekt kann die innere, private Klasse instanziieren. Der resultierende Verweis wird in der Methode TurbineStarten zum Aufruf der Methode StartEngine benutzt. Obwohl der Aufrufer nichts von der Existenz der Klasse Triebwerk weiß, wird er Nutzen aus dieser Klasse ziehen können und die Reaktion auf den Aufruf zu spüren bekommen – und sei es nur die Ausgabe im Konsolenfenster.

Gegenseitige Abhängigkeit von äußerer und innerer Klasse

Wir wollen jetzt unsere Überlegungen hinsichtlich der Abhängigkeit der beiden Klassen Flugzeug und Triebwerk auf die Spitze treiben. Wenn wir es ganz streng sehen, müssen wir feststellen, dass ein Flugzeug ein Triebwerk besitzt und ein Triebwerk auch in einem ganz bestimmten Flugzeug eingebaut ist. Deklarieren wir die innere Klasse Triebwerk als public und instanziieren sie mit


Flugzeug.Triebwerk turbine = new Flugzeug.Triebwerk(545, 320);

fehlt dem Triebwerk die Information darüber, in welchem Flugzeug es eingebaut ist.

Um eine eindeutige Zuordnung zu gewährleisten, übergeben wir dem Konstruktoraufruf des Triebwerk-Objekts die Referenz auf das Flugzeug-Objekt, dem das Triebwerk zugeordnet wird. Diese wird in einem privaten Feld flugzeug der Klasse Triebwerk gespeichert.

Sehen wir uns nun die Klassendefinition von Flugzeug an, die sich auf das Wesentlichste beschränkt. Damit wir uns später im Testcode vom Erfolg überzeugen können, wird ein Flugzeug durch einen Namen beschrieben, der dem Konstruktor der Klasse Flugzeug bei der Instanziierung übergeben wird. Außerdem wird die Eigenschaft Motor mit der Rückgabe der Referenz auf das Triebwerk-Objekt angeboten, über die ein Benutzer Zugriff auf die Methoden des Triebwerks hat.


// -------------------------------------------------------------
// Beispiel: ...Kapitel 4\InnereKlassen
// -------------------------------------------------------------
public class Flugzeug {
  private Triebwerk motor;
  private string name;
  public Flugzeug(string bezeichnung)   {
    motor = new Triebwerk(this);
    this.name = bezeichnung;
  }
  public Triebwerk Motor {
    get {return this.motor;}
  }
  public void Starten() {
    Console.WriteLine("Das Flugzeug {0} startet", this.name);
  }
  // ------------ innere Klasse Triebwerk ---------------
  public class Triebwerk {
    private Flugzeug flugzeug;
    public Triebwerk(Flugzeug flg) {
      this.flugzeug = flg;
      this.flugzeug.motor = this;
    }
    public void StartEngine() {
      Console.WriteLine("Das Triebwerk wird gestartet");
    }
  }
}

Wir können nun die Klasse Flugzeug instanziieren und die Eigenschaft Motor aufrufen, die ihrerseits die Referenz auf das dem Flugzeug zugeordnete Triebwerk liefert. Damit können wir das Triebwerk starten:


Flugzeug flg = new Flugzeug("Piper");
flg.Motor.StartEngine();

Wir haben auch die Möglichkeit, die innere Klasse Triebwerk zu instanziieren. Dabei müssen wir dem Konstruktor die Referenz auf ein konkretes Flugzeug-Objekt übergeben:


Flugzeug flg = new Flugzeug("Piper");
Flugzeug.Triebwerk trb = new Flugzeug.Triebwerk(flg);

Grundsätzlich birgt dieser Aufruf die Gefahr der Inkonsistenz, denn wir erzeugen ein neues Triebwerk auf Basis des konkreten Flugzeugs flg. Dieses Flugzeug-Objekt ist aber bereits im Besitz eines Triebwerks, das im Konstruktor erzeugt worden ist und dessen Referenz im Feld motor vorgehalten wird. Wir müssen dem Flugzeug-Objekt mitteilen, dass das erste Triebwerk »ausgetauscht« und durch ein neues ersetzt wird. Um dem Flugzeug die Existenz des neuen Triebwerks mitzuteilen, enthält der Konstruktor der Klasse Triebwerk folgende Anweisung:


this.flugzeug.motor = this;

Nun ist der Kreis vollständig geschlossen. Jedes Triebwerk ist unzweifelhaft einem bestimmten Flugzeug zugeordnet, und jedes Flugzeug hat auch nur ein bestimmtes Triebwerk.

Zugriffsmodifizierer einer inneren Klasse

Wie Sie oben gesehen haben, unterscheidet sich eine öffentlich (als public oder als internal) deklarierte innere Klasse nicht wesentlich von einer separaten Klassendefinition. Ein Benutzer sieht die innere Klasse und kann sie instanziieren. Das Konzept der verschachtelten Klassen, nämlich die konkrete Zugehörigkeit zu einem Objekt der umschließenden Klasse, wird dadurch aufgeweicht.

Ganz anders verhält sich eine als private deklarierte innere Klasse. Die Sichtbarkeit ist nur auf das umschließende äußere Objekt begrenzt, und die Bindung ist eindeutig und kann nicht aufgebrochen werden. Erst jetzt kann der Verbund zwischen dem vom Aufrufer erkennbaren Objekt und seinem zugeordneten Teilobjekt die konzeptuellen Stärken voll ausspielen.

Ist die umschließende äußere Klasse nicht als sealed definiert, kann von ihr abgeleitet werden. Normalerweise dürfte die Subklasse gleichermaßen Interesse an einem Objekt der eingebetteten Klasse haben. Weil sich als private deklarierte Komponenten jedoch der Sichtbarkeit der Subklasse entziehen, sollte die eingebettete Klasse in einer ableitbaren äußeren Klasse immer als protected deklariert sein.



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# 2010

Visual C# 2010
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 2010
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.


Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern