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

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 eine neue Datei namens BankAccount.cs im Klassenverzeichnis .

Diese Datei enthält die Definition eines Bankkontos. Die Objektorientierte Programmierung organisiert Code durch Erstellen von Typen in Form von Klassen. 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. Auszahlungen können nicht zu einem negativen Gleichgewicht führen.

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, den Sie erstellen. Sämtliche Inhalte zwischen { und }, die der Klassendeklaration folgen, definieren den Zustand und das Verhalten der Klasse. Es gibt fünf Mitglieder der BankAccount 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 Methoden 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 BankAccount Typs bedeutet, einen Konstruktor zu definieren, der diese Werte zuweisen soll. Ein Konstruktor ist ein Element, das denselben Namen wie die Klasse hat. Es 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;
}

Der vorherige Code identifiziert die Eigenschaften des Objekts, das erstellt wird, indem er den this Qualifizierer enthält. Dieser Qualifizierer ist in der Regel optional und ausgelassen. Sie könnten auch geschrieben haben:

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

Der this Qualifizierer ist nur erforderlich, wenn eine lokale Variable oder ein Parameter denselben Namen wie dieses Feld oder diese Eigenschaft hat. Der this Qualifizierer wird während des restlichen Artikels ausgelassen, es sei denn, es ist erforderlich.

Konstruktoren werden bei der Erstellung eines Objekts mit new aufgerufen. Ersetzen Sie die Zeile Console.WriteLine("Hello World!"); in Program.cs durch den folgende Code (ersetzen Sie <name> durch 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 besteht darin, 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;

Das accountNumberSeed ist ein Datenmitglied. 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 static, was bedeutet, dass es von allen BankAccount Objekten freigegeben wird. 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. Das Nachverfolgen jeder Transaktion hat einige Vorteile, um einfach das Guthaben für jede Transaktion zu aktualisieren. Der Verlauf kann zum Überwachen aller Transaktionen und Verwalten täglicher Kontostände verwendet werden. Das Berechnen des Gleichgewichts aus dem Verlauf aller Transaktionen, wenn erforderlich, stellt sicher, dass fehler in einer einzelnen Transaktion, die behoben werden, ordnungsgemäß im Gleichgewicht auf der nächsten Berechnung dargestellt werden.

Zunächst erstellen wir einen neuen Typ, um eine Transaktion darzustellen. Die Transaktion ist ein einfacher Typ, der keine Verantwortung 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 BankAccount.cs durch den folgenden Code:

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

        return balance;
    }
}

In diesem Beispiel wird ein wichtiger Aspekt der Eigenschaften gezeigt. 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 Ausgleich muss positiv sein, und jede Abhebung darf kein negatives Gleichgewicht schaffen.

Diese Regeln führen das Konzept von Ausnahmen ein. Die Standardmethode, die angibt, dass eine Methode ihre Arbeit nicht erfolgreich abschließen kann, besteht darin, eine Ausnahme zu auslösen. Der Typ der Ausnahme und die zugeordnete Nachricht beschreiben den Fehler. Hier löst die MakeDeposit Methode eine Ausnahme aus, wenn der Betrag der Einzahlung nicht größer als 0 ist. Die MakeWithdrawal Methode löst eine Ausnahme aus, wenn der Auszahlungsbetrag nicht größer als 0 ist, oder wenn die Anwendung der Auszahlung ein negatives Guthaben bewirkt. 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 löst 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 diesen Code, indem Sie in Ihrer Main Methode einige Einzahlungen und Auszahlungen hinzufügen, gefolgt vom Code, der ein neues BankAccounterstellt:

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 als Nächstes, dass Sie Fehlerbedingungen erfassen, indem Sie versuchen, ein Konto mit einem negativen Konto 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 try 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 vor der Deklaration invalidAccount ihrer 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();
}

Der Verlauf verwendet die StringBuilder Klasse, um eine Zeichenfolge zu formatieren, 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: