Arbeiten mit .NET: Grundlagen der OOP/ Ein erster Einblick

In diesem Kapitel werden wir die praktischen Überlegungen zu den Vorteilen von Objekten in die .NET-Programmiersprachen übertragen und uns eine Klassendefinition verschaffen.

Die Namen

Bearbeiten

In diesem Buch richten wir uns nach den.NET-Namenskonventionen, wobei lieber englische Bezeichner verwendet werden. Natürlich ist in deutschen Veröffentlichungen (also auch hier in de.Wikibooks) die deutsche Sprache vorzuziehen. Aber zum einen benutzt die EDV sowieso viele englische Fachbegriffe, die auch in fortlaufenden Texten passen (bei uns z.B. String oder EventHandler). Zum anderen erzeugt die IDE selbständig neue Bezeichner, beispielsweise:

  1. Eine Textbox in einem Formular nennt der Programmierer EingebenGehalt.
  2. Dazu wird das Ereignis Validate zugewiesen, das prüfen soll, ob es sich um einen zulässigen Wert handelt.
  3. Die IDE macht daraus einen EventHandler namens EingebenGehalt_Validate.

Solchen denglischen Mischmasch wollen wir vermeiden. Dann benutzen wir lieber durchgehend englische Bezeichnungen.

Die Definition der Klasse

Bearbeiten

In .NET ist jede Klasse implizit (also ohne dass man es erwähnen muss) von der grundlegenden Klasse System.Object abgeleitet und erbt alle ihre Eigenheiten. Zusätzlich müssen wir alle Eigenheiten festlegen, die unsere Klasse ausmachen.

Bitte wundern Sie sich nicht, wenn die Erläuterungen in diesem Kapitel sehr knapp sind und viele Begriffe noch nicht erklärt werden. Es handelt sich hier wirklich nur um einen „ersten Einblick“, damit wir in den nächsten Kapiteln mit den Grundbegriffen von Klassen arbeiten können.

Die Klasse selbst

Bearbeiten

Zur eigentlichen Definition gehören das Schlüsselwort der Sprache, dass es sich um eine Klasse handelt – also Class/class –, ihr Name Car, der Bereich dessen, was sie umfasst, und eine Angabe, wer alles die Klasse benutzen darf – nämlich Public.

C#-Quelltext
public class Car
{
  // hierher kommen Einzelheiten
}
VB.NET-Quelltext
Public Class Car
  ' hierher kommen Einzelheiten
End Class

Zum Bereich der Einzelheiten gehören Konstante, Felder, Eigenschaften, Methoden und Ereignisse. Alle diese Bestandteile werden mit dem Sammelbegriff Member bezeichnet. Eine deutsche Übersetzung ist nicht üblich. (Es kommt vor, dass Member mit den Feldern gleichgesetzt wird, aber das ist nicht korrekt.) Darunter sind einfach „alle Mitglieder“ der Klasse, also alle ihre Bestandteile gemeint.

Alles, was in den nächsten Abschnitten erwähnt ist, gehört in diesen Bereich. Die vollständige Klassendefinition, wie sie auch im Projekt Mein neues Auto verwendet wird, ist gegen Ende dieses Kapitels enthalten.

Ein konkretes Objekt

Bearbeiten

Während eine Klasse die allgemeine Beschreibung eines Typs ist, handelt es sich bei einem Objekt um ein bestimmtes Ding von diesem Typ. Man sagt auch, dass es sich um eine Instanz der Klasse handelt.

Die Klasse beschreibt, welche Eigenschaften der Typ haben kann und was damit gemacht werden kann. Für das Objekt wird der Inhalt der Eigenschaften festgelegt; die möglichen Verfahren werden mit bzw. für das Objekt ausgeführt.

 

Merke
Eine Klasse ist die (abstrakte) Beschreibung eines Teils der Wirklichkeit.
Ein Objekt ist ein (konkreter) Teil der Wirklichkeit, dessen Inhalt mit den Einzelheiten der Klasse beschrieben wird und mit dem die Verfahren der Klasse genutzt werden können.
Eine Instanz einer Klasse ist ebenfalls ein bestimmtes Objekt.


Um mit einem Objekt arbeiten zu können, müssen wir es erzeugen; auch die Formulierungen „erstellen“ oder „konstruieren“ sind üblich. So könnten wir unser Objekt bekommen (wie es so ähnlich in der Program-Datei der Anwendung Mein neues Auto gemacht wird:

Ein Objekt erzeugen
C#-Quelltext
Car mycar = new Car();
VB.NET-Quelltext
Dim mycar As Car = New Car()

Zum Erstellen eines Objekts gehören also:

  • die Angabe des Typs, nämlich: Car
  • ein Name für unser Objekt, nämlich: mycar
  • der Befehl zum Erstellen, nämlich: new
  • der Aufruf des Konstruktors, nämlich der Name der Klasse mit angehängten Klammern: Car()

Wir brauchen also (mindestens) einen Konstruktor, stellen das aber noch zurück. Denn eine Klasse ohne jede Funktion ist ziemlich irrelevant.

Hinweis: Der Konstruktor aus dem vorigen Beispiel kann bei unserer Klasse nicht genutzt werden, wie bei den Konstruktoren noch erläutert wird.

Eigenschaften und Felder

Bearbeiten

Betrachten wir zu dieser Klasse ein paar mögliche Einzelheiten:

  • Die folgenden Angaben müssen bei der Konstruktion eines Autos festgelegt werde; aber sie können und dürfen während seiner Lebenszeit nicht mehr geändert werden:
    Fahrgestellnummer (ID), Fahrzeugtyp, Rechts- oder Linkslenkung
  • Die folgenden Angaben dürfen auch später geändert werden:
    Farbe, Anzahl der Sitze
  • Die folgende Angabe kann nur mit besonderem Arbeitsaufwand geändert werden:
    der Typ des Motors
  • Die folgende Angabe kann nicht direkt festgelegt werden, sondern ergibt sich indirekt:
    aktuelle Fahrgeschwindigkeit (nämlich abhängig von Beschleunigung und Verzögerung), Blinker rechts und links

Um diese unterschiedlichen Situationen zu berücksichtigen, wird zwischen Eigenschaften und Feldern[1] unterschieden.

  • Eine Eigenschaft (Property) gibt einen Wert an, der auch außerhalb der Klasse bekannt ist: mindestens zum Abruf (wie Fahrgestellnummer/ID, Typ oder Geschwindigkeit) oder auch zur Festlegung (wie die Farbe).
  • Ein Feld (Field) gibt einen Wert an, der innerhalb der Klasse für einen bestimmten Zustand steht (wie die aktuelle Geschwindigkeit).

Also definieren wir die benötigten Felder:

Interner Zustand: Felder
C#-Quelltext
private int id;
private Color paint;
private string type;
private string motortype;
private bool isLeftSteered;
private int numberOfSeats;
private bool leftSignal = false;
private bool rightSignal = false;
private int speed = 0;
VB.NET-Quelltext
Private m_id As Integer
Private m_paint As Color
Private m_type As String
Private m_motortype As String
Private m_isLeftSteered As Boolean
Private m_numberOfSeats As Integer
Private leftSignal As Boolean = False
Private rightSignal As Boolean = False
Private m_speed As Integer = 0

Lesehinweis: Beachten Sie die Bezeichnungen der Felder und Eigenschaften:

  • Bei Programmiersprachen wie C++ und C#, die zwischen Groß- und Kleinschreibung unterscheiden, fängt der Name einer Eigenschaft mit einem Großbuchstaben und der Name eines Feldes mit einem Kleinbuchstaben an.
  • Bei Programmiersprachen wie VB oder Delphi, die diese Unterscheidung nicht kennen, fängt der Name einer Eigenschaft mit einem Großbuchstaben an; beim Namen des entsprechenden Feldes wird "m_" vorangesetzt.

In Code-Auszügen beachten wir diese Unterschiede selbstverständlich. Damit fortlaufender Text leichter lesbar ist, benutzen wir die Schreibweise mit "m_".

Eigenschaften

Bearbeiten

Einige Angaben stehen auf Abruf („nur-Lesen“) zur Verfügung:

Informationen zum inneren Zustand: Eigenschaften (nur-Lesen)
C#-Quelltext
/// ID liefert die Fabrikationsnummer des Wagens.
public int ID {
	get { return id; }
}
VB.NET-Quelltext
''' ID liefert die Fabrikationsnummer des Wagens.
Public ReadOnly Property ID() As Integer
	Get
		Return m_id
	End Get
End Property

In gleicher Weise werden die folgenden Eigenschaften definiert: Speed für m_speed, Type für m_Type, Motortype für m_Motortype, IsLeftSteered für m_IsLeftSteered.

Einige Angaben dürfen auch „von außerhalb“ direkt geändert werden:

Den inneren Zustand beeinflussen: Eigenschaften festlegen
C#-Quelltext
public Color Paint {
	get { return paint; }
	set { paint = value; }
}
VB.NET-Quelltext
Public Property Paint() As Color
	Get
		Return m_paint
	End Get
	Set
		m_paint = value
	End Set
End Property

In neueren Versionen ist die folgende Kurzschreibweise zulässig.

Kurzschreibweise für Eigenschaften
C#-Quelltext
// zulässig ab C# 3.0
public Color Paint { get; set; }
VB.NET-Quelltext
' zulässig ab VB 10.0
Public Property Paint() As Color

Diese Schreibweise wird erst später erläutert. Sie sollten sich jetzt nur merken: Mit dieser einfachen Deklaration einer Eigenschaft wird innerhalb der Klasse automatisch ein passendes Feld eingerichtet.

Bei dieser Gelegenheit kann die Klasse sicherstellen, dass bestimmte Bedingungen erfüllt sind. Ein Auto (genauer: ein Pkw) ist nur dann einer, wenn es mindestens einen Sitz für den Fahrer gibt und nicht mehr als 9 Sitze eingebaut werden können.

Den inneren Zustand beeinflussen: Eigenschaften mit Bedingungen
C#-Quelltext
/// NumberOfSeats gibt die Anzahl der Sitze zurück oder legt diese fest.
/// Es sind nur Zahlen von 1 bis 9 zulässig; der Standardwert ist 4.
public int NumberOfSeats {
	get { return numberOfSeats; }
	set 
	{ 
		if (value >= 1 && value <= 9)
			numberOfSeats = value;
		else
			numberOfSeats = 4;
	}
}
VB.NET-Quelltext
''' NumberOfSeats gibt die Anzahl der Sitze zurück oder legt diese fest.
''' Es sind nur Zahlen von 1 bis 9 zulässig; der Standardwert ist 4.
Public Property NumberOfSeats() As Integer
	Get
		Return m_numberOfSeats
	End Get
	Set
		If value >= 1 And value <= 9 Then
			m_numberOfSeats = value
		Else
			m_numberOfSeats = 4
		End If
	End Set
End Property


Weitere Angaben dürfen nur durch besondere Maßnahmen geändert werden:

Den inneren Zustand beeinflussen: nur über Methoden
C#-Quelltext
/// Das Auto soll beschleunigt werden.
/// <param name="diff">Betrag, um den die Geschwindkeit erhöht wird</param>
public void Accelerate(int diff)
{
	if(diff >= 0)
		speedChange(diff);
}
VB.NET-Quelltext
''' Das Auto soll beschleunigt werden.
''' <param name="diff">Betrag, um den die Geschwindkeit erhöht wird</param>
Public Sub Accelerate(ByVal diff As Integer)
	If diff >= 0 Then
		speedChange(diff)
	End If
End Sub

In gleicher Weise wird durch Delay das Bremsen simuliert. In ähnlicher Weise werden Rechts- und Linksblinker gesteuert: durch SetLeftSignal der linke, durch SetRightSignal der rechte und durch SetBothSignals beide Blinker.

Ein Objekt benutzen

Bearbeiten

Mit diesen Definitionen können wir die Eigenschaften und Felder unseres konkreten Objekts mycar beeinflussen:

C#-Quelltext
// beschleunigen um 40 km/h
mycar.Accelerate(40);
// rechten Blinker ausschalten
mycar.SetRightSignal(false);
// neu lackieren
mycar.Paint = Color.Red;
VB.NET-Quelltext
' beschleunigen um 40 km/h
mycar.Accelerate(40)
' rechten Blinker ausschalten
mycar.SetRightSignal(false)
' neu lackieren
mycar.Paint = Color.Red

Aber die folgenden Maßnahmen sind nicht zulässig, weil wir die betreffenden Werte nicht direkt ändern dürfen:

  Unzulässiger Zugriff auf Felder
C#-Quelltext
// Geschwindigkeit auf 50 km/h setzen
mycar.Speed = 50;
// rechten Blinker ausschalten
mycar.rightSignal = false;
// einen anderen Motor registrieren
mycar.Motortype = "1288cm³ 64 kW";
VB.NET-Quelltext
' Geschwindigkeit auf 50 km/h setzen
mycar.Speed = 50
' rechten Blinker ausschalten
mycar.rightSignal = false
' einen anderen Motor registrieren
mycar.Motortype = "1288cm³ 64 kW"


Zugriffsrechte

Bearbeiten

Nebenbei haben wir noch erfahren, dass der Zugriff auf Elemente einer Klasse beschränkt werden kann.

  • Public ist öffentlich: Diese Eigenschaften und Methoden können „von außen“ benutzt werden.
  • Private ist privat: Diese Elemente können nur „innerhalb“ der Klasse benutzt werden.

Sowohl für die Klasse selbst als auch für alle Elemente gibt es viele Varianten, die unter den Begriffen Zugriffsrechte und Modifizierer behandelt werden.

Konstruktoren

Bearbeiten

Bisher haben wir von einer Besonderheit von .NET profitiert. Wenn eine Klasse keinen Konstruktor besitzt, dann stellt der Compiler beim Übersetzen des Quellcodes selbst einen zur Verfügung, den Standardkonstruktor.

Ein Konstruktor beschreibt, wie genau das Objekt erzeugt werden soll. Steht dort nichts, gibt es auch keine Besonderheiten. Alle Felder des Objekts werden automatisch mit ihren Standardwerten initialisiert, also z.B. m_ID mit Null oder m_IsLeftSteered auf false. Auch wenn wir selbst keinen Konstruktor festlegen, schreibt der Compiler einen in den erzeugten Code, und zwar genauso, wie wir es mit dem folgenden Code auch tun könnten:

Der Standardkonstruktor
C#-Quelltext
public Car()
{
}
VB.NET-Quelltext
Public Sub New()
End Sub

Das hat bisher durchaus ausgereicht. Allerdings haben wir uns jetzt die Nur-Lesen-Eigenschaften ID und Typ eingebrockt. Und so, wie sie jetzt dastehen, sind sie schlicht unerreichbar. Direkt ändern dürfen wir sie nicht: Als Eigenschaften sind die Werte schreibgeschützt, und die Felder sind privat, also von außen nicht erreichbar.

Hier helfen uns weitere Konstruktoren, mit denen einer oder mehrere Werte direkt festgelegt werden. Grundsätzlich gilt:

  • Ein Standardkonstruktor ohne Parameter (also ohne Einzelheiten in den runden Klammern) ist immer vorhanden.
  • Der Standardkonstruktor kann – wie im vorstehenden Code – leer sein oder Inhalt bekommen.
  • Es können weitgehend beliebig weitere Konstruktoren vorgesehen werden.

Die genaue Schreibweise hängt dabei von der Programmiersprache ab.

Konstruktoren in C#

Die wichtigsten Regeln für die Definition eines Konstruktors lauten:

  • Der Konstruktor muss den Zugriff freigeben; in der Regel ist er public definiert.
  • Der Konstruktor heißt genauso wie die Klasse.
  • Er hat keinen Rückgabewert; das bedeutet: er gibt ein Objekt des eigenen Typs zurück.
  • In den geschweiften Klammern steht, was während der Konstruktion zu erledigen ist.

So sieht also der Standardkonstruktor aus:

public Car() {}

Beheben wir also das Problem mit der ID und dem Typ. Logischerweise wird der Typ eines Autos ein einziges Mal direkt bei der Produktion festgelegt, ebenso wie die Fahrgestellnummer (also unsere ID); später kann dies nicht mehr geändert werden. Außerdem ist es sinnvoll, den Motortyp direkt anzugeben. Wenn wir das auch machen wollen, müssen wir unseren Konstruktor nur minimal verändern.

public Car(string type, string motortype, int id)
{
	this.type = type;
	this.motortype = motortype;
	this.id = id;
}

Wundern Sie sich nicht über die abweichende Definition in der vorbereiteten Klasse; das ist eine Optimierung, die später besprochen wird.

Ein neues Auto kann dann mit folgendem Befehl erzeugt werden (Typ = "ASM 342", ID = 316).

Car secondCar = new Car("ASM 342", "1288 cm³ 64 kW", 316);

Wenn wir schon dabei sind, können wir auch gleich alle anderen Standardwerte festlegen, die für die Produktion sowieso benötigt werden:

public Car(string type, string motortype, int id, bool isLeft, int numberofseats, Color paint)
{
	this.type = type;
	this.motortype = motortype;
	this.id = id;
	this.isLeftSteered = isLeft;
	this.paint = paint;
	this.NumberOfSeats = numberofseats;
}

Ein neues Auto wird also mit folgendem Befehl erzeugt, wobei die Angaben vollständig und in exakt der gleichen Reihenfolge angegeben werden müssen:

Car thirdCar = new Car("ASM 342", "1288 cm³ 64 kW", 316, true, 4, Color.Yellow);

In dieser Weise kann man noch viele weitere Konstruktoren vorsehen. Es mag schließlich Gelegenheiten geben, bei denen man die Farbe und die Ausstattung erst später festlegen will; oder man will diese Festlegung jemand anderem überlassen.

Schließlich wollen wir uns (und alle Programmierer, die diese Klasse benutzen) dazu zwingen, immer einen der besonderen Konstruktoren zu verwenden, damit die erforderlichen Angaben auch tatsächlich eingetragen werden. Das gelingt dadurch, dass der Standardkonstruktor ungültig gemacht wird, genauer: er wird auf private statt auf public gesetzt und ist damit "von außen" nicht mehr erreichbar:

private Car() {}

Darauf bezog sich der Hinweis ganz oben, dass der Konstruktor aus dem vorigen Beispiel nicht genutzt werden kann.

Konstruktoren in VB

Die wichtigsten Regeln für die Definition eines Konstruktors lauten:

  • Der Konstruktor muss den Zugriff freigeben; in der Regel ist er Public definiert.
  • Der Konstruktor ist eine spezielle Sub (d.h. Subroutine, also eine Unterfunktion) und heißt New.
  • Er hat keinen Rückgabewert; das bedeutet: er gibt ein Objekt des eigenen Typs zurück.
  • Bis zum End Sub steht, was während der Konstruktion zu erledigen ist.

So sieht also der Standardkonstruktor aus:

Private Sub New()
End Sub

Beheben wir also das Problem mit der ID und dem Typ. Logischerweise wird der Typ eines Autos ein einziges Mal direkt bei der Produktion festgelegt, ebenso wie die Fahrgestellnummer (also unsere ID); später kann dies nicht mehr geändert werden. Außerdem ist es sinnvoll, den Motortyp direkt anzugeben. Wenn wir das auch machen wollen, müssen wir unseren Konstruktor nur minimal verändern.

Public Sub New(ByVal type As String, ByVal motortype As String, ByVal id As Integer)
	m_type = type
	m_motortype = motortype
	m_id = id
End Sub

Wundern Sie sich nicht über die abweichende Definition in der vorbereiteten Klasse; das ist eine Optimierung, die später besprochen wird.

Ein neues Auto kann dann mit folgendem Befehl erzeugt werden (Typ = "ASM 342", ID = 316).

Dim secondCar As Car = New Car("ASM 342", "1288 cm³ 64 kW", 316);

Wenn wir schon dabei sind, können wir auch gleich alle anderen Standardwerte festlegen, die für die Produktion sowieso benötigt werden:

Public Sub New(ByVal type As String, ByVal motortype As String, ByVal id As Integer, _
		ByVal isLeft As Boolean, ByVal numberofseats As Integer, ByVal paint As Color)
	m_type = type
	m_motortype = motortype
	m_id = id
	m_isLeftSteered = isLeft
	m_paint = paint
	NumberOfSeats = numberofseats
End Sub

Ein neues Auto wird also mit folgendem Befehl erzeugt, wobei die Angaben vollständig und in exakt der gleichen Reihenfolge angegeben werden müssen:

Dim thirdCar As Car = New Car("ASM 342", "1288 cm³ 64 kW", 316, true, 4, Color.Yellow);

In dieser Weise kann man noch viele weitere Konstruktoren vorsehen. Es mag schließlich Gelegenheiten geben, bei denen man die Farbe und die Ausstattung erst später festlegen will; oder man will diese Festlegung jemand anderem überlassen.

Schließlich wollen wir uns (und alle Programmierer, die diese Klasse benutzen) dazu zwingen, immer einen der besonderen Konstruktoren zu verwenden, damit die erforderlichen Angaben auch tatsächlich eingetragen werden. Das gelingt dadurch, dass der Standardkonstruktor ungültig gemacht wird, genauer: er wird auf Private statt auf Public gesetzt und ist damit "von außen" nicht mehr erreichbar:

Private Sub New()
End Sub

Darauf bezog sich der Hinweis ganz oben, dass der Konstruktor aus dem vorigen Beispiel nicht genutzt werden kann.

Destruktoren

Bearbeiten

Wenn es Konstruktoren gibt, dann muss es doch auch Destruktoren geben, oder nicht? Stimmt, aber in der Regel muss sich der Programmierer damit nicht befassen, sondern kann die Arbeit dem .NET Framework überlassen.

Sollte jemand doch einen eigenen Destruktor benötigen, so ist dies mit folgendem Code Fragment möglich:

eigener Destruktor
C#-Quelltext
~Car()
{
  // hierher kommen Einzelheiten
}
VB.NET-Quelltext
Protected Overrides Sub Finalize()
    ' hierher kommen Einzelheiten
    MyBase.Finalize()
End Sub


Methoden

Bearbeiten

Was wäre unser Auto ohne die Fähigkeit, den Motor starten, Gas geben, bremsen oder blinken zu können? All diese Fähigkeiten sind Aktionen, die man mit einem Auto im echten Leben durchführen kann. Unter .NET heißen diese Aktionen Methoden.

Bei verschiedenen Programmiersprachen wird auch unter .NET zwischen Prozeduren (ohne Rückgabewert) und Funktionen (mit Rückgabewert) unterschieden. Wir benutzen diese Unterscheidung nur dann, wenn wir gezielt eine Sprache besprechen.

Deklaration ohne Rückgabewert

Bearbeiten

Bereiten wir also für die Klasse Car ein paar Methoden vor. Wir beschränken uns zunächst auf einfache Methoden ohne Rückgabewert und ohne Inhalt.

Deklaration von Methoden
C#-Quelltext
/// Das Auto soll beschleunigt werden.
public void Accelerate(int diff)        {  }

/// Das Auto soll verzögert werden.
public void Delay(int diff)             {  }
 
/// Der linke Blinker wird betätigt.
public void SetLeftSignal(bool value)   {  }
//  analog für den rechten Blinker und für beide, nämlich für die Warnblinkleuchte
Delphi.Prism-Quelltext
/// Das Auto soll beschleunigt werden.
method Accelerate(diff: Integer); public;

/// Das Auto soll verzögert werden.
method Delay(diff: Integer); public;
 
/// Der linke Blinker wird betätigt.
method SetLeftSignal(value: Boolean); public;
//  analog für den rechten Blinker und für beide, nämlich für die Warnblinkleuchte
VB.NET-Quelltext
''' Das Auto soll beschleunigt werden.
Public Sub Accelerate(ByVal diff As Integer)
End Sub
 
''' Das Auto soll verzögert werden.
Public Sub Delay(ByVal diff As Integer)
End Sub
 
''' Der linke Blinker wird betätigt.
Public Sub SetLeftSignal(ByVal value As Boolean)
End Sub
''  analog für den rechten Blinker und für beide, nämlich für die Warnblinkleuchte

Schauen wir uns nun die Methode SetLeftSignal etwas genauer an:

  • Mit public wird wieder angegeben, dass diese Methode „von außen“, also für ein konkretes Objekt benutzt werden darf.
  • Außerdem wird angegeben, dass bei diesen Methoden kein Wert als Ergebnis der Aktion zurückgegeben wird.
    • Bei C++/C# besagt dies der Begriff void.
    • Bei VB sagt der Begriff Sub, dass es sich um eine Subroutine ohne Rückgabewert handelt.
    • Bei Delphi.Prism kann der Begriff procedure dafür genutzt werden. Vorzuziehen ist der neuere Begriff method; weil kein Rückgabetyp angegeben ist, gilt dies als Methode ohne Rückgabewert.
  • Zusätzlich stehen innerhalb der Klammern Parameter (auch Argumente genannt): Dabei handelt es sich um einen oder mehrere Werte, die von der Methode verarbeitet werden sollen.
    Beim Beispiel des linken Blinkers handelt es sich darum, ob der Blinker eingeschaltet werden soll – also auf true zu setzen ist – oder ob er ausgeschaltet werden soll – also auf false gesetzt wird.
    Wir brauchen also einen Parameter vom Typ Boolean, der innerhalb der Methode mit dem Namen value verwendet wird.

Parameter sind also der Input einer Methode, der Rückgabewert (hier noch nicht vorhanden) der Output.

Es fehlt alles, was die Methode eigentlich erledigen soll. Stellen wir das noch kurz zurück; es folgt im übernächsten Abschnitt Der Inhalt einer Methode.

Deklaration mit Rückgabewert

Bearbeiten

Jede Klasse besitzt die Methode ToString, die einen String mit dem Inhalt des Objekts anzeigt. Standardmäßig erhalten wir jedoch nur den Namen der Klasse:

 // Aufruf:
 Console.WriteLine( mycar.ToString() );
 // Ausgabe:
 Wikibooks.CSharp.Mein_neues_Auto.Car

Das ist doch sehr ungenau. Also erstellen wir uns eine eigene Methode ToString, die die vorgegebene ersetzen soll (der Fachausdruck lautet überschreiben bzw. override) und gleich noch eine weitere, die den aktuellen Status der Blinklichter anzeigen soll.

Deklaration von Methoden
C#-Quelltext
public override string ToString()   {  }
public string ShowSignals()         {  }
Delphi.Prism-Quelltext
method ToString(): String; public; override;
method ShowSignals(): String; public;
VB.NET-Quelltext
Public Overloads Overrides Function ToString() As String
End Function

Public Function ShowSignals() As String
End Function

Schauen wir uns die Methode ToString etwas genauer an:

  • Mit public wird wieder angegeben, dass diese Methode „von außen“, also für ein konkretes Objekt benutzt werden darf.
  • Mit override (bei VB zusammen mit Overloads) wird die Standardmethode ToString überschrieben.
  • Außerdem wird angegeben, dass ein Wert als Ergebnis der Aktion zurückgegeben wird und zwar durch den Datentyp. Dabei kann jeder Datentyp verwendet werden: neben den einfachen wie String oder Integer auch komplexe wie Form (Formular) oder eigene wie Car.
    • Bei C++/C# besagt der Begriff string, dass wir einen String erhalten.
    • Bei VB sagt der Begriff Function, dass es sich um eine Funktion mit Rückgabewert handelt; der Typ wird durch As String angefügt.
    • Bei Delphi.Prism kann der Begriff function dafür genutzt werden. Vorzuziehen ist der neuere Begriff method; weil nach einem Doppelpunkt der Rückgabetyp String angegeben ist, gilt dies als Methode mit Rückgabewert.
  • Innerhalb der Klammern stehen keine Parameter, weil die Methode nur den inneren Zustand des Objekts beschreiben soll.

Mit diesen Vorarbeiten können wir nun festlegen, was die Methoden eigentlich erledigen sollen.

Der Inhalt einer Methode

Bearbeiten

Innerhalb der Definition einer Methode fehlt nun noch die Erklärung, was genau getan werden soll. Dies wird innerhalb der Definition eingetragen:

  • bei C++/C# zwischen die geschweiften Klammern {...}
  • bei VB zwischen die beiden Zeilen, also vor das End Sub bzw. End Function
  • bei Delphi.Prism zwischen begin und end

Schreiben wir nun die Aktion, die ausgeführt werden soll, wenn die Geschwindigkeit erhöht oder verringert werden soll.

Die Methoden zur Geschwindigkeit (Variante 1)
C#-Quelltext
public void Accelerate(int diff)
{
	if (diff > 0)
		speed = speed + diff;
}
		
public void Delay(int diff)
{
	if (diff > 0)
		speed = speed - diff;
}
Delphi.Prism-Quelltext
method Accelerate(diff: integer); 
begin
	if (diff > 0) then
		m_Speed = m_Speed + diff;
end;

method Delay(diff: integer); 
begin
	if (diff > 0) then
		m_Speed = m_Speed - diff;
end;
VB.NET-Quelltext
Public Sub Accelerate(ByVal diff As Integer)
	If (diff > 0) Then
		m_Speed = m_Speed + diff
	End If
End Sub
 
Public Sub Delay(ByVal diff As Integer)
	If (diff > 0) Then
		m_Speed = m_Speed - diff
	End If
End Sub

Der Wert, um den sich die Geschwindigkeit ändern soll, wird hier zur bisherigen Geschwindigkeit einfach addiert bzw. subtrahiert.

In beiden Fällen wird fast dasselbe erledigt; im mathematischen Sinn gibt es überhaupt keinen Unterschied. Also können wir beide Abläufe vereinheitlichen; wir brauchen nur eine zusätzliche Methode (intern, also private), die die Geschwindigkeit einheitlich regelt. Dies kann auch auf die Wirklichkeit übertragen werden, wenn beim Bremsen der Benzinzufluss reduziert wird.

Die Methoden zur Geschwindigkeit (Variante 2)
C#-Quelltext
public void Accelerate(int diff)
{
	if (diff > 0)
		speedChange(diff);
}
		
public void Delay(int diff)
{
	if (diff > 0)
		speedChange(- diff);
}

private void speedChange(int diff)
{
	speed = speed + diff;
}
Delphi.Prism-Quelltext
method Accelerate(diff: integer); 
begin
	if (diff > 0) then
		speedChange(diff);
end;

method Delay(diff: integer); 
begin
	if (diff > 0) then
		speedChange(- diff);
end;

method speedChange(diff: integer);    // private deklariert
begin
	m_Speed = m_Speed + diff;
end;
VB.NET-Quelltext
Public Sub Accelerate(ByVal diff As Integer)
	If (diff > 0) Then
		speedChange(diff)
	End If
End Sub
 
Public Sub Delay(ByVal diff As Integer)
	If (diff > 0) Then
		speedChange(- diff)
	End If
End Sub

Private Sub speedChange(ByVal diff As Integer)
	m_speed = m_speed + diff
End Sub

Wenn die Differenz positiv ist, wird die Geschwindigkeit erhöht; wenn sie negativ ist, wird sie verringert – genau das, was man mit Beschleunigen und Verzögern verbindet.

Bei diesem Beispiel kann man streiten, welcher Weg einfacher und schöner ist. Stellen Sie sich aber Arbeitsabläufe vor, die bis auf eine Kleinigkeit (hier: positiver oder negativer Wert) identisch sind. Da sollte man sich von Anfang an zu mehr Übersichtlichkeit zwingen.

Quellcode: Klasse Car

Bearbeiten

Unsere ganze Klasse sieht damit jetzt so aus:

Wenn Sie den Code herunterladen und speichern wollen, dann drücken Sie mit Rechtsklick auf den Link und wählen "Ziel speichern unter...".

Die Klasse Car
C#-Quelltext
using System;
using System.Drawing;

namespace Wikibooks.CSharp.Mein_neues_Auto
{
	/// <summary>
	/// Die Klasse Car beschreibt ein Auto.
	/// </summary>
	public class Car
	{
		#region Felder
		private int id;
		private Color paint;
		private string type;
		private string motortype;
		private bool isLeftSteered;
		private int numberOfSeats;
		private bool leftSignal = false;
		private bool rightSignal = false;
		private int speed = 0;
		#endregion
		
		#region Eigenschaften
		/// <summary>
		/// ID liefert die Fabrikationsnummer des Wagens.
		/// </summary>
		public int ID {
			get { return id; }
		}
		
		/// <summary>
		/// Paint ist die aktuelle Farbe der Lackierung oder legt diese fest.
		/// </summary>
		public Color Paint {
			get { return paint; }
			set { paint = value; }
		}
		
		/// <summary>
		/// Type ist der Typ des Autos (kann nur im Konstruktor festgelegt werden).
		/// </summary>
		public string Type {
			get { return type; }
		}
		
		/// <summary>
		/// Motortype ist der Typ des eingebauten Motors. Er kann nur 
		/// beim Konstruktor festgelegt oder mit SetMotortype geändert werden.
		/// </summary>
		public string Motortype {
			get { return motortype; }
		}
		
		/// <summary>
		/// IsLeftSteered = true, wenn das Lenkrad links eingebaut ist, oder
		/// = false, wenn das Lenkrad rechts eingebaut ist.
		/// Kann nur im Konstruktor festgelegt werden.
		/// </summary>
		public bool IsLeftSteered {
			get { return isLeftSteered; }
		}
		
		/// <summary>
		/// NumberOfSeats gibt die Anzahl der Sitze zurück oder legt diese fest.
		/// Es sind nur Zahlen von 1 bis 9 zulässig; der Standardwert ist 4.
		/// </summary>
		public int NumberOfSeats {
			get { return numberOfSeats; }
			set 
			{ 
				if (value >= 1 && value <= 9)
					numberOfSeats = value;
				else
					numberOfSeats = 4;
			}
		}
		
		/// <summary>
		/// Gibt die aktuelle Geschwindkeit zurück.
		/// Änderungen sind nur mit Accelerate() und Delay() möglich.
		/// </summary>
		public int Speed {
			get { return speed; }
		}
		
		#endregion

		#region Konstruktoren
		/// <summary>
		/// Der Standardkonstruktor ohne Parameter darf nicht benutzt werden.
		/// </summary>
		private Car()
		{			
		}
		
		/// <summary>
		/// Dieser Konstruktor übernimmt die wichtigsten Werte und setzt zusätzlich
		/// Standardwerte: Linkslenkung, 4 Sitze, Farbe weiß
		/// </summary>
		/// <param name="type">Typ des Autos</param>
		/// <param name="motortype">Typ des Motors</param>
		/// <param name="id">Fabrikationsnummer</param>
		public Car(string type, string motortype, int id)
		{
			this.type = type;
			this.motortype = motortype;
			this.id = id;
			this.isLeftSteered = true;
			this.paint = Color.White;
			this.NumberOfSeats = 4;
		}
		
		/// <summary>
		/// Dieser Konstruktor übernimmt sämtliche vorgesehenen Werte.
		/// </summary>
		/// <param name="type">Typ des Autos</param>
		/// <param name="motortype">Typ des Motors</param>
		/// <param name="id">Fabrikationsnummer</param>
		/// <param name="isLeft">Position des Lenkrads: true = links, false = rechts</param>
		/// <param name="numberofseats">Anzahl der Sitze</param>
		/// <param name="paint">Farbe der Lackierun</param>
		public Car(string type, string motortype, int id, bool isLeft,
		           int numberofseats, Color paint)
		{
			this.type = type;
			this.motortype = motortype;
			this.id = id;
			this.isLeftSteered = isLeft;
			this.paint = paint;
			this.NumberOfSeats = numberofseats;
		}
		#endregion

		#region Public Methoden
		/// <summary>
		/// Das Auto soll beschleunigt werden.
		/// </summary>
		/// <param name="diff">Betrag, um den die Geschwindkeit erhöht wird</param>
		public void Accelerate(int diff)
		{
			if (diff > 0)
				speedChange(diff);
		}
		
		/// <summary>
		/// Das Auto soll verzögert werden.
		/// </summary>
		/// <param name="diff">Betrag, um den die Geschwindkeit verringert wird</param>
		public void Delay(int diff)
		{
			if (diff > 0)
				speedChange(-diff);
		}
		
		/// <summary>
		/// Der linke Blinker wird betätigt.
		/// </summary>
		/// <param name="value">true = einschalten / false = ausschalten</param>
		public void SetLeftSignal(bool value)
		{
			leftSignal = value;
			rightSignal = false;
		}

		/// <summary>
		/// Der rechte Blinker wird betätigt.
		/// </summary>
		/// <param name="value">true = einschalten / false = ausschalten</param>
		public void SetRightSignal(bool value)
		{
			leftSignal = false;
			rightSignal = value;
		}
		
		/// <summary>
		/// Beide Blinker werden betätigt.
		/// </summary>
		/// <param name="value">true = einschalten / false = ausschalten</param>
		public void SetBothSignals(bool value)
		{
			leftSignal = value;
			rightSignal = value;
		}
		
		/// <summary>
		/// Der Typ des Motors wird geändert.
		/// </summary>
		/// <param name="value">Der Name des neuen Motortyps.</param>
		public void SetMotortype(string value)
		{
			if (! String.IsNullOrEmpty(value) )
				motortype = value;
		}
		#endregion
		
		#region Private Methoden
		private void speedChange(int diff)
		{
			// reagiere auf die Veränderung der Geschwindigkeit
			speed = speed + diff;
		}
		#endregion
		
		#region Aktuelle Werte anzeigen
		/// <summary>
		/// Diese Methode gibt alle wichtigen Informationen zur aktuellen Instanz zurück.
		/// </summary>
		/// <returns>Ein String mit den Informationen in zwei Zeilen</returns>
		public override string ToString()
		{
			return String.Format("Car: ID={0}, Typ={1}, Motor={2}, {3},\r\n{4} Sitze, {5}",
			                    id, type, motortype, paint, numberOfSeats,
			                    this.IsLeftSteered ? "Linkssteuerung" : "Rechtssteuerung");
		}
		
		/// <summary>
		/// Diese Methode gibt den aktuellen Status von Geschwindkeit und
		/// Blinker an der Konsole aus.
		/// </summary>
		public void ShowValues()
		{
			Console.Write("Geschwindigkeit: ");
			Console.Write(speed);
			Console.Write(" km/h - ");
			Console.WriteLine( ShowSignals() );
		}
		
		/// <summary>
		/// Diese Methode gibt den aktuellen Status des Blinkers an.
		/// </summary>
		/// <returns>Texthinweis auf Warnblinker/Links/Rechts/Aus.</returns>
		public string ShowSignals()
		{
			string result;
			if (leftSignal && rightSignal)
				result = "Warnblinker ein";
			else if (leftSignal)
				result = "Links blinken";
			else if (rightSignal)
				result = "Rechts blinken";
			else
				result = "Blinker aus";
			return result;
		}
		#endregion
		
	}
}
VB.NET-Quelltext
Imports System
Imports System.Drawing

Namespace Wikibooks.VBNet.Mein_neues_Auto
	''' <summary>
	''' Die Klasse Car beschreibt ein Auto.
	''' </summary>
	Public Class Car
		
		#Region "Felder"
		Private m_id As Integer
		Private m_paint As Color
		Private m_type As String
		Private m_motortype As String
		Private m_isLeftSteered As Boolean
		Private m_numberOfSeats As Integer
		Private leftSignal As Boolean = False
		Private rightSignal As Boolean = False
		Private m_speed As Integer = 0
		#End Region
		
		#Region "Eigenschaften"
		''' <summary>
		''' ID liefert die Fabrikationsnummer des Wagens.
		''' </summary>
		Public ReadOnly Property ID() As Integer
			Get
				Return m_id
			End Get
		End Property
		
		''' <summary>
		''' Paint ist die aktuelle Farbe der Lackierung oder legt diese fest.
		''' </summary>
		Public Property Paint() As Color
			Get
				Return m_paint
			End Get
			Set
				m_paint = value
			End Set
		End Property
		
		''' <summary>
		''' Type ist der Typ des Autos (kann nur im Konstruktor festgelegt werden).
		''' </summary>
		Public ReadOnly Property Type() As String
			Get
				Return m_type
			End Get
		End Property
		
		''' <summary>
		''' Motortype ist der Typ des eingebauten Motors. Er kann nur
		''' beim Konstruktor festgelegt oder mit SetMotortype geändert werden.
		''' </summary>
		Public ReadOnly Property Motortype() As String
			Get
				Return m_motortype
			End Get
		End Property
		
		''' <summary>
		''' IsLeftSteered = true, wenn das Lenkrad links eingebaut ist, oder
		''' = false, wenn das Lenkrad rechts eingebaut ist.
		''' Kann nur im Konstruktor festgelegt werden.
		''' </summary>
		Public ReadOnly Property IsLeftSteered() As Boolean
			Get
				Return m_isLeftSteered
			End Get
		End Property
		
		''' <summary>
		''' NumberOfSeats gibt die Anzahl der Sitze zurück oder legt diese fest.
		''' Es sind nur Zahlen von 1 bis 9 zulässig; der Standardwert ist 4.
		''' </summary>
		Public Property NumberOfSeats() As Integer
			Get
				Return m_numberOfSeats
			End Get
			Set
				If value >= 1 And value <= 9 Then
					m_numberOfSeats = value
				Else
					m_numberOfSeats = 4
				End If
			End Set
		End Property
		
		''' <summary>
		''' Gibt die aktuelle Geschwindkeit zurück.
		''' Änderungen sind nur mit Accelerate() und Delay() möglich.
		''' </summary>
		Public ReadOnly Property Speed() As Integer
			Get
				Return m_speed
			End Get
		End Property
		
		#End Region
		
		#Region "Konstruktoren"
		''' <summary>
		''' Der Standardkonstruktor ohne Parameter darf nicht benutzt werden.
		''' </summary>
		Private Sub New()
		End Sub
		
		''' <summary>
		''' Dieser Konstruktor übernimmt die wichtigsten Werte und setzt zusätzlich
		''' Standardwerte: Linkslenkung, 4 Sitze, Farbe weiß
		''' </summary>
		''' <param name="type">Typ des Autos</param>
		''' <param name="motortype">Typ des Motors</param>
		''' <param name="id">Fabrikationsnummer</param>
		Public Sub New(ByVal type As String, ByVal motortype As String, ByVal id As Integer)
			m_type = type
			m_motortype = motortype
			m_id = id
			m_isLeftSteered = True
			m_paint = Color.White
			NumberOfSeats = 4
		End Sub
		
		''' <summary>
		''' Dieser Konstruktor übernimmt sämtliche vorgesehenen Werte.
		''' </summary>
		''' <param name="type">Typ des Autos</param>
		''' <param name="motortype">Typ des Motors</param>
		''' <param name="id">Fabrikationsnummer</param>
		''' <param name="isLeft">Position des Lenkrads: true = links, false = rechts</param>
		''' <param name="numberofseats">Anzahl der Sitze</param>
		''' <param name="paint">Farbe der Lackierun</param>
		Public Sub New(ByVal type As String, ByVal motortype As String, ByVal id As Integer, _
				ByVal isLeft As Boolean, ByVal numberofseats As Integer, ByVal paint As Color)
			m_type = type
			m_motortype = motortype
			m_id = id
			m_isLeftSteered = isLeft
			m_paint = paint
			NumberOfSeats = numberofseats
		End Sub
		#End Region
		
		#Region "Public Methoden"
		''' <summary>
		''' Das Auto soll beschleunigt werden.
		''' </summary>
		''' <param name="diff">Betrag, um den die Geschwindkeit erhöht wird</param>
		Public Sub Accelerate(ByVal diff As Integer)
			If (diff > 0) Then
				speedChange(diff)
			End If
		End Sub
		
		''' <summary>
		''' Das Auto soll verzögert werden.
		''' </summary>
		''' <param name="diff">Betrag, um den die Geschwindkeit verringert wird</param>
		Public Sub Delay(ByVal diff As Integer)
			If (diff > 0) Then
				speedChange(-diff)
			End If
		End Sub
		
		''' <summary>
		''' Der linke Blinker wird betätigt.
		''' </summary>
		''' <param name="value">true = einschalten / false = ausschalten</param>
		Public Sub SetLeftSignal(ByVal value As Boolean)
			leftSignal = value
			rightSignal = False
		End Sub
		
		''' <summary>
		''' Der rechte Blinker wird betätigt.
		''' </summary>
		''' <param name="value">true = einschalten / false = ausschalten</param>
		Public Sub SetRightSignal(ByVal value As Boolean)
			leftSignal = False
			rightSignal = value
		End Sub
		
		''' <summary>
		''' Beide Blinker werden betätigt.
		''' </summary>
		''' <param name="value">true = einschalten / false = ausschalten</param>
		Public Sub SetBothSignals(ByVal value As Boolean)
			leftSignal = value
			rightSignal = value
		End Sub
		
		''' <summary>
		''' Der Typ des Motors wird geändert.
		''' </summary>
		''' <param name="value">Der Name des neuen Motortyps.</param>
		Public Sub SetMotortype(ByVal value As String)
			If Not [String].IsNullOrEmpty(value) Then
				m_motortype = value
			End If
		End Sub
		#End Region
		
		#Region "Private Methoden"
		Private Sub speedChange(ByVal diff As Integer)
			' reagiere auf die Veränderung der Geschwindigkeit
			m_speed = m_speed + diff
		End Sub
		#End Region
		
		#Region "Aktuelle Werte anzeigen"
		''' <summary>
		''' Diese Methode gibt alle wichtigen Informationen zur aktuellen Instanz zurück.
		''' </summary>
		''' <returns>Ein String mit den Informationen in zwei Zeilen</returns>
		Public Overloads Overrides Function ToString() As String
			Return String.Format("Car: ID={0}, Typ={1}, Motor={2}, {3}," _
				& Environment.NewLine & "{4} Sitze, {5}", _
				m_id, m_type, m_motortype, m_paint, m_numberOfSeats, _
				IIf(Me.IsLeftSteered,"Linkssteuerung","Rechtssteuerung"))
		End Function
		
		''' <summary>
		''' Diese Methode gibt den aktuellen Status von Geschwindkeit und
		''' Blinker an der Konsole aus.
		''' </summary>
		Public Sub ShowValues()
			Console.Write("Geschwindigkeit: ")
			Console.Write(m_speed)
			Console.Write(" km/h - ")
			Console.WriteLine(ShowSignals())
		End Sub
		
		''' <summary>
		''' Diese Methode gibt den aktuellen Status des Blinkers an.
		''' </summary>
		''' <returns>Texthinweis auf Warnblinker/Links/Rechts/Aus.</returns>
		Public Function ShowSignals() As String
			Dim result As String
			If leftSignal And rightSignal Then
				result = "Warnblinker ein"
			ElseIf leftSignal Then
				result = "Links blinken"
			ElseIf rightSignal Then
				result = "Rechts blinken"
			Else
				result = "Blinker aus"
			End If
			Return result
		End Function
		#End Region
		
	End Class
End Namespace

Die Inhalte dieser Klasse werden zum Anfang einer jeden Programmiersprache für das Einstiegsprogramm genutzt.

Siehe auch

Bearbeiten

Bei Wikipedia gibt es die folgenden Artikel:

Hinweise
  1. Der Begriff Felder hat in Mathematik und EDV noch weitere Bedeutungen, z.B. einer Liste gleichartiger Werte. Wir benutzen diesen Begriff nur für die Beschreibung des inneren Zustands einer Klasse bzw. eines Objekts.