Erkunden der objektorientierten Programmierung mit Klassen und Objekten

In diesem Tutorial erstellen Sie eine Konsolenanwendung und die grundlegenden objektorientierten Funktionen, die Teil der C#-Sprache sind.

Voraussetzungen

Für dieses Tutorial wird vorausgesetzt, dass Sie einen Computer für die lokale Entwicklung eingerichtet haben. Unter Windows, Linux oder macOS können Sie die .NET CLI zum Programmieren, Erstellen und Ausführen von Anwendungen verwenden. Unter Windows können Sie Visual Studio 2019 verwenden. Setupanweisungen finden Sie unter Einführung in .NET-Entwicklungstools.

Erstellen Ihrer Anwendung

Erstellen Sie in einem Terminalfenster ein Verzeichnis namens classes. Dort werden Sie Ihre Anwendung erstellen. Wechseln Sie in dieses Verzeichnis, und geben Sie dotnet new console im Konsolenfenster ein. Dieser Befehl erstellt die Anwendung. Öffnen Sie Program.cs. Es sollte wie folgt aussehen:

// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");

In diesem Tutorial erstellen Sie neue Typen, die ein Bankkonto darstellen. Entwickler definieren jede Klasse in der Regel in einer anderen Textdatei. Dies erleichtert die Verwaltung bei zunehmender Programmgröße. Erstellen Sie im Verzeichnis Classes eine neue Datei mit dem Namen BankAccount.cs.

Diese Datei enthält die Definition eines Bankkontos. Die objektorientierte Programmierung organisiert Code, indem Typen in Form von Klassen erstellt werden. Diese Klassen enthalten den Code, der eine bestimmte Entität darstellt. Die BankAccount-Klasse stellt ein Bankkonto dar. Der Code implementiert bestimmte Vorgänge mittels Methoden und Eigenschaften. In diesem Tutorial unterstützt das Bankkonto dieses Verhalten:

  1. Es enthält eine 10-stellige Zahl, die das Bankkonto eindeutig identifiziert.
  2. Es enthält eine Zeichenfolge, die den bzw. die Namen des Besitzers speichert.
  3. Der Kontostand kann abgerufen werden.
  4. Es akzeptiert Einzahlungen.
  5. Es akzeptiert Abbuchungen.
  6. Der anfängliche Kontostand muss positiv sein.
  7. Abbuchungen dürfen nicht in einem negativen Kontostand resultieren.

Definieren des Bankkontotyps

Sie können beginnen, indem Sie die Grundlagen einer Klasse erstellen, die dieses Verhalten definiert. Erstellen Sie eine neue Datei mit dem Befehl File:New. Nennen Sie sie BankAccount.cs. Fügen Sie der Datei BankAccount.cs folgenden Code hinzu:

namespace Classes;

public class BankAccount
{
    public string Number { get; }
    public string Owner { get; set; }
    public decimal Balance { get; }

    public void MakeDeposit(decimal amount, DateTime date, string note)
    {
    }

    public void MakeWithdrawal(decimal amount, DateTime date, string note)
    {
    }
}

Bevor wir fortfahren, lassen Sie uns anschauen, was Sie erstellt haben. Die namespace-Deklaration ist eine Möglichkeit, Ihren Code logisch zu organisieren. Da dieses Tutorial relativ klein ist, platzieren Sie den gesamten Code in einen einzigen Namespace.

public class BankAccount definiert die Klasse oder den Typ, die/den Sie erstellen. Sämtliche Inhalte zwischen { und }, die der Klassendeklaration folgen, definieren den Zustand und das Verhalten der Klasse. Es gibt fünf Member der -Klasse. Die ersten drei sind Eigenschaften. Eigenschaften sind Datenelemente und können Code aufweisen, der eine Überprüfung oder andere Regeln erzwingt. Die letzten beiden sind Methoden. Methoden sind Codeblöcke, die eine einzelne Funktion ausführen. Das Lesen der Namen der einzelnen Member sollte Ihnen oder anderen Entwicklern genug Informationen liefern, um zu verstehen, welche Aufgabe die Klasse hat.

Eröffnen eines neuen Kontos

Die erste zu implementierende Funktion ist das Eröffnen eines Bankkontos. Wenn ein Kunde ein Konto eröffnet, muss er einen anfänglichen Kontostand bereitstellen und Informationen über den/die Besitzer dieses Kontos angeben.

Das Erstellen eines neuen Objekts des Typs bedeutet, BankAccount einen BankAccount zu definieren, der diese Werte zuweist. Ein Konstruktor ist ein Member, der den gleichen Namen wie die -Klasse hat. Er wird verwendet, um Objekte dieses Klassentyps zu initialisieren. Fügen Sie dem BankAccount-Typ den folgenden Konstruktor hinzu,. Platzieren Sie folgenden Code oberhalb der Deklaration von MakeDeposit:

public BankAccount(string name, decimal initialBalance)
{
    this.Owner = name;
    this.Balance = initialBalance;
}

Konstruktoren werden bei der Erstellung eines Objekts mit new aufgerufen. Ersetzen Sie die Zeile Console.WriteLine("Hello World!"); in Console.WriteLine("Hello World!"); durch den folgenden Code (ersetzen Sie durch <name> Ihren Namen):

using Classes;

var account = new BankAccount("<name>", 1000);
Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance.");

Wir führen nun aus, was wir bisher erstellt haben. Wenn Sie Visual Studio verwenden, klicken Sie im Menü Debuggen auf Ohne Debuggen starten. Wenn Sie eine Befehlszeile verwenden, geben Sie dotnet run in dem Verzeichnis ein, wo Sie das Projekt erstellt haben.

Haben Sie bemerkt, dass die Kontonummer leer ist? Es ist höchste Zeit, dies zu ändern. Die Kontonummer sollten zugewiesen werden, wenn das Objekt erstellt wird. Jedoch sollte nicht der Aufrufende für das Erstellen verantwortlich sein. Der BankAccount-Klassencode sollte wissen, wie neue Kontonummern zugewiesen werden. Eine einfache Möglichkeit hierzu ist, mit einer 10-stelligen Zahl zu beginnen. Lassen Sie sie bei jeder Erstellung eines neuen Kontos erhöhen. Speichern Sie schließlich die aktuelle Kontonummer, wenn ein Objekt erstellt wird.

Fügen Sie der BankAccount-Klasse eine Memberdeklaration hinzu. Platzieren Sie die folgende Codezeile nach der öffnenden geschweiften Klammer { am Anfang der BankAccount-Klasse:

private static int accountNumberSeed = 1234567890;

Dies ist ein Datenelement. Es ist private, d.h. der Zugriff darauf ist nur über Code in der BankAccount-Klasse möglich. Dies ist eine Möglichkeit, die öffentlichen Verantwortlichkeiten (z. B. Besitz einer Kontonummer) von der privaten Implementierung (wie Kontonummern generiert werden) zu trennen. Es ist auch als static definiert, wird also von allen BankAccount-Objekten gemeinsam genutzt. Der Wert einer nicht statischen Variable ist für jede Instanz des BankAccount-Objekts eindeutig. Fügen Sie dem Konstruktor die folgenden zwei Zeilen hinzu, um die Kontonummer zuzuweisen. Platzieren Sie sie nach der Zeile, die this.Balance = initialBalance enthält:

this.Number = accountNumberSeed.ToString();
accountNumberSeed++;

Geben Sie dotnet run ein, um die Ergebnisse anzuzeigen.

Erstellen von Einzahlungen und Abbuchungen

Um ordnungsgemäß zu funktionieren, muss Ihre Bankkontoklasse Einzahlungen und Abbuchungen akzeptieren. Einzahlungen und Abbuchungen implementieren Sie, indem Sie eine Erfassung jeder Transaktion für das Konto erstellen. Dies ist vorteilhafter, als den Kontostand bei jeder Transaktion einfachen zu aktualisieren. Der Verlauf kann zum Überwachen aller Transaktionen und Verwalten täglicher Kontostände verwendet werden. Indem der Kontostand ggf. aus dem Verlauf aller Transaktionen berechnet wird, werden etwaige Korrekturen von Fehlern in einer einzelnen Transaktion bei der nächsten Berechnung im Kontostand ordnungsgemäß widergespiegelt.

Zunächst erstellen wir einen neuen Typ, um eine Transaktion darzustellen. Dies ist ein einfacher Typ, der keine Verantwortlichkeiten hat. Er benötigt einige Eigenschaften. Erstellen Sie eine neue Datei mit dem Namen Transaction.cs. Fügen Sie ihr folgenden Code hinzu:

namespace Classes;

public class Transaction
{
    public decimal Amount { get; }
    public DateTime Date { get; }
    public string Notes { get; }

    public Transaction(decimal amount, DateTime date, string note)
    {
        Amount = amount;
        Date = date;
        Notes = note;
    }
}

Nun fügen wir eine List<T> von Transaction-Objekten der BankAccount-Klasse hinzu. Fügen Sie die folgende Deklaration nach dem Konstruktor in ihrer BankAccount.cs-Datei hinzu:

private List<Transaction> allTransactions = new List<Transaction>();

Nun werden Sie Balance korrekt berechnen. Der aktuelle Saldo kann durch Summieren der Werte aller Transaktionen ermittelt werden. Mit dem aktuellen Code können Sie nur den anfänglichen Saldo des Kontos abrufen. Sie müssen daher die Eigenschaft Balance aktualisieren. Ersetzen Sie die Zeile public decimal Balance { get; } in public decimal Balance { get; } durch den folgenden Code:

public decimal Balance
{
    get
    {
        decimal balance = 0;
        foreach (var item in allTransactions)
        {
            balance += item.Amount;
        }

        return balance;
    }
}

Dieses Beispiel zeigt einen wichtigen Aspekt von Eigenschaften. Jetzt berechnen Sie den Kontostand, wenn ein anderer Programmierer den Wert anfordert. Die Berechnung zählt alle Transaktionen auf und gibt die Summe als aktuellen Kontostand zurück.

Implementieren Sie nun die MakeDeposit- und MakeWithdrawal-Methode. Diese Methoden erzwingen die letzten beiden Regeln: Der anfängliche Kontostand muss positiv sein, und eine Abbuchung darf nicht in einem negativen Kontostand resultieren.

Dadurch wird das Konzept der Ausnahmen eingeführt. Standardmäßig wird eine Ausnahme ausgelöst, um anzuzeigen, dass eine Methode ihre Aufgabe nicht erfolgreich ausführen kann. Der Typ der Ausnahme und die zugeordnete Nachricht beschreiben den Fehler. Hier löst die MakeDeposit -Methode eine Ausnahme aus, wenn der Betrag der Einlage nicht größer als 0 ist. Die MakeWithdrawal -Methode löst eine Ausnahme aus, wenn der Abbuchungsbetrag nicht größer als 0 ist oder wenn das Anwenden der Abbuchung zu einem negativen Saldo führt. Fügen Sie nach der Deklaration der allTransactions-Liste den folgenden Code hinzu:

public void MakeDeposit(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of deposit must be positive");
    }
    var deposit = new Transaction(amount, date, note);
    allTransactions.Add(deposit);
}

public void MakeWithdrawal(decimal amount, DateTime date, string note)
{
    if (amount <= 0)
    {
        throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
    }
    if (Balance - amount < 0)
    {
        throw new InvalidOperationException("Not sufficient funds for this withdrawal");
    }
    var withdrawal = new Transaction(-amount, date, note);
    allTransactions.Add(withdrawal);
}

Die throw-Anweisung throw eine Ausnahme aus. Die Ausführung des aktuellen Block wird beendet, und die Steuerung wird an den ersten übereinstimmenden catch-Block übertragen, der in der Aufrufliste gefunden wurde. Sie fügen einen catch-Block hinzu, um diesen Code etwas später zu testen.

Der Konstruktor sollte so geändert werden, dass er eine anfängliche Transaktion hinzufügt, anstatt den Kontostand direkt zu aktualisieren. Da Sie die MakeDeposit-Methode bereits geschrieben haben, rufen Sie sie aus dem Konstruktor auf. Der fertige Konstruktor sollte wie folgt aussehen:

public BankAccount(string name, decimal initialBalance)
{
    Number = accountNumberSeed.ToString();
    accountNumberSeed++;

    Owner = name;
    MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
}

DateTime.Now ist eine Eigenschaft, die das aktuelle Datum und die Uhrzeit zurückgibt. Testen Sie dies durch Hinzufügen von ein paar Einzahlungen und Abbuchungen in Ihrer Main-Methode und verfolgen Sie den Code, der einen neuen BankAccount erstellt:

account.MakeWithdrawal(500, DateTime.Now, "Rent payment");
Console.WriteLine(account.Balance);
account.MakeDeposit(100, DateTime.Now, "Friend paid me back");
Console.WriteLine(account.Balance);

Testen Sie anschließend, ob Sie Fehlerbedingungen abfangen, indem Sie versuchen, ein Konto mit einem negativen Kontostand zu erstellen. Fügen Sie den folgenden Code nach dem vorhergehenden Code hinzu:

// Test that the initial balances must be positive.
BankAccount invalidAccount;
try
{
    invalidAccount = new BankAccount("invalid", -55);
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine("Exception caught creating account with negative balance");
    Console.WriteLine(e.ToString());
    return;
}

Sie verwenden die Anweisungen und catch, um einen Codeblock zu markieren, der Ausnahmen auslösen kann, und um die erwarteten Fehler abzufangen. Mit dem gleichen Verfahren können Sie den Code testen, der bei einem negativen Kontostand eine Ausnahme auslöst. Fügen Sie den folgenden Code am Ende der Main-Methode hinzu:

// Test for a negative balance.
try
{
    account.MakeWithdrawal(750, DateTime.Now, "Attempt to overdraw");
}
catch (InvalidOperationException e)
{
    Console.WriteLine("Exception caught trying to overdraw");
    Console.WriteLine(e.ToString());
}

Speichern Sie die Datei, und geben Sie dotnet run zum Testen ein.

Herausforderung – Protokollieren aller Transaktionen

Um dieses Tutorial abzuschließen, können Sie die GetAccountHistory-Methode schreiben, die eine Zeichenfolge (string) für den Transaktionsverlauf erstellt. Fügen Sie diese Methode dem BankAccount-Typ hinzu:

public string GetAccountHistory()
{
    var report = new System.Text.StringBuilder();

    decimal balance = 0;
    report.AppendLine("Date\t\tAmount\tBalance\tNote");
    foreach (var item in allTransactions)
    {
        balance += item.Amount;
        report.AppendLine($"{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
    }

    return report.ToString();
}

Hier wird die StringBuilder-Klasse zum Formatieren einer Zeichenfolge verwendet, die eine Zeile für jede Transaktion enthält. Sie haben den Zeichenfolgen-Formatierungscode in diesen Tutorials bereits gesehen. \t ist ein neues Zeichen. Damit wird ein Tabulator zum Formatieren der Ausgabe hinzugefügt.

Fügen Sie diese Zeile Program.cs zum Testen hinzu:

Console.WriteLine(account.GetAccountHistory());

Führen Sie das Programm aus, um die Ergebnisse zu sehen.

Nächste Schritte

Wenn Sie nicht weiterkommen, sehen Sie sich die Quelle für dieses Tutorial in unserem GitHub-Repository an.

Sie können mit dem Tutorial zur objektorientierten Programmierung fortfahren.

Weitere Informationen zu diesen Begriffen finden Sie in diesen Artikeln: