Arbeiten mit .NET: OOP/ Einführung/ 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:
- Eine Textbox in einem Formular nennt der Programmierer EingebenGehalt.
- Dazu wird das Ereignis Validate zugewiesen, das prüfen soll, ob es sich um einen zulässigen Wert handelt.
- 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.
public class Car
{
// hierher kommen Einzelheiten
}
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:
Car mycar = new Car();
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).
Felder
[Bearbeiten]Also definieren wir die benötigten 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;
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:
/// ID liefert die Fabrikationsnummer des Wagens.
public int ID {
get { return id; }
}
''' 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:
public Color Paint {
get { return paint; }
set { paint = value; }
}
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.
// zulässig ab C# 3.0
public Color Paint { get; set; }
' 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.
/// 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;
}
}
''' 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:
/// 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);
}
''' 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:
// beschleunigen um 40 km/h
mycar.Accelerate(40);
// rechten Blinker ausschalten
mycar.SetRightSignal(false);
// neu lackieren
mycar.Paint = Color.Red;
' 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:
// Geschwindigkeit auf 50 km/h setzen
mycar.Speed = 50;
// rechten Blinker ausschalten
mycar.rightSignal = false;
// einen anderen Motor registrieren
mycar.Motortype = "1288cm³ 64 kW";
' 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:
public Car()
{
}
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.
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.
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:
~Car()
{
// hierher kommen Einzelheiten
}
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.
/// 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
/// 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
''' 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.
public override string ToString() { }
public string ShowSignals() { }
method ToString(): String; public; override;
method ShowSignals(): String; public;
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.
public void Accelerate(int diff)
{
if (diff > 0)
speed = speed + diff;
}
public void Delay(int diff)
{
if (diff > 0)
speed = speed - diff;
}
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;
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.
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;
}
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;
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...".
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
}
}
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
- ↑ 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.