Utforska objektorienterad programmering med klasser och objekt

I den här självstudien skapar du ett konsolprogram och ser de grundläggande objektorienterade funktionerna som ingår i C#-språket.

Förutsättningar

  • Vi rekommenderar Visual Studio för Windows eller Mac. Du kan ladda ned en kostnadsfri version från nedladdningssidan för Visual Studio. Visual Studio innehåller .NET SDK.
  • Du kan också använda Visual Studio Code-redigeraren . Du måste installera den senaste .NET SDK separat.
  • Om du föredrar en annan redigerare måste du installera den senaste .NET SDK:en.

Skapa ditt program

Skapa en katalog med namnet Klasser med hjälp av ett terminalfönster. Du skapar ditt program där. Ändra till den katalogen och skriv dotnet new console i konsolfönstret. Det här kommandot skapar ditt program. Öppna Program.cs. Den bör se ut så här:

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

I den här självstudien ska du skapa nya typer som representerar ett bankkonto. Vanligtvis definierar utvecklare varje klass i en annan textfil. Det gör det enklare att hantera när ett program växer i storlek. Skapa en ny fil med namnet BankAccount.cs i katalogen Klasser .

Den här filen innehåller definitionen av ett bankkonto. Objektorienterad programmering organiserar kod genom att skapa typer i form av klasser. Dessa klasser innehåller den kod som representerar en specifik entitet. Klassen BankAccount representerar ett bankkonto. Koden implementerar specifika åtgärder via metoder och egenskaper. I den här självstudien har bankkontot stöd för det här beteendet:

  1. Den har ett tiosiffrigt nummer som unikt identifierar bankkontot.
  2. Den har en sträng som lagrar ägarnas namn eller namn.
  3. Saldot kan hämtas.
  4. Den tar emot insättningar.
  5. Den accepterar uttag.
  6. Den inledande balansen måste vara positiv.
  7. Uttag kan inte resultera i ett negativt saldo.

Definiera bankkontotypen

Du kan börja med att skapa grunderna i en klass som definierar det beteendet. Skapa en ny fil med kommandot File:New . Ge den namnet BankAccount.cs. Lägg till följande kod i din BankAccount.cs-fil :

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)
    {
    }
}

Innan vi går vidare ska vi ta en titt på vad du har byggt. Deklarationen namespace är ett sätt att logiskt organisera koden. Den här självstudien är relativt liten, så du placerar all kod i ett namnområde.

public class BankAccount definierar klassen eller typen som du skapar. Allt i { och } som följer klassdeklarationen definierar klassens tillstånd och beteende. Det finns fem medlemmar i BankAccount klassen. De första tre är egenskaper. Egenskaper är dataelement och kan ha kod som framtvingar verifiering eller andra regler. De två sista är metoder. Metoder är kodblock som utför en enda funktion. Att läsa namnen på var och en av medlemmarna bör ge tillräckligt med information för att du eller en annan utvecklare ska förstå vad klassen gör.

Öppna ett nytt konto

Den första funktionen att implementera är att öppna ett bankkonto. När en kund öppnar ett konto måste de ange ett initialt saldo och information om ägaren eller ägarna av det kontot.

Att skapa ett nytt objekt av typen BankAccount innebär att definiera en konstruktor som tilldelar dessa värden. En konstruktor är en medlem som har samma namn som klassen. Den används för att initiera objekt av den klasstypen. Lägg till följande konstruktor till BankAccount typen. Placera följande kod ovanför deklarationen av MakeDeposit:

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

Föregående kod identifierar egenskaperna för det objekt som skapas genom att inkludera kvalificeringen this . Den kvalificeraren är vanligtvis valfri och utelämnas. Du kan också ha skrivit:

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

Kvalificeraren this krävs endast när en lokal variabel eller parameter har samma namn som fältet eller egenskapen. Kvalificeringen this utelämnas under resten av den här artikeln om det inte är nödvändigt.

Konstruktorer anropas när du skapar ett objekt med .new Ersätt raden Console.WriteLine("Hello World!"); i Program.cs med följande kod (ersätt <name> med ditt namn):

using Classes;

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

Nu ska vi köra det du har byggt hittills. Om du använder Visual Studio väljer du Starta utan att felsöka från felsökningsmenyn. Om du använder en kommandorad skriver dotnet run du i katalogen där du har skapat projektet.

Märkte du att kontonumret är tomt? Det är dags att fixa det. Kontonumret ska tilldelas när objektet skapas. Men det bör inte vara uppringarens ansvar att skapa det. Klasskoden BankAccount bör veta hur du tilldelar nya kontonummer. Ett enkelt sätt är att börja med ett tiosiffrigt tal. Öka den när varje nytt konto skapas. Lagra slutligen det aktuella kontonumret när ett objekt skapas.

Lägg till en medlemsdeklaration i BankAccount klassen. Placera följande kodrad efter den inledande klammerparentesen {BankAccount i början av klassen:

private static int s_accountNumberSeed = 1234567890;

accountNumberSeed är en datamedlem. Det är private, vilket innebär att den bara kan nås med kod i BankAccount klassen. Det är ett sätt att skilja det offentliga ansvaret (som att ha ett kontonummer) från den privata implementeringen (hur kontonummer genereras). Det är också static, vilket innebär att det delas av alla BankAccount objekt. Värdet för en icke-statisk variabel är unikt för varje instans av BankAccount objektet. accountNumberSeed är ett private static fält och har därmed prefixet s_ enligt C#-namngivningskonventionerna. Fältet s som anger static och _ anger private . Lägg till följande två rader i konstruktorn för att tilldela kontonumret. Placera dem efter raden som säger this.Balance = initialBalance:

Number = s_accountNumberSeed.ToString();
s_accountNumberSeed++;

Skriv dotnet run för att se resultatet.

Skapa insättningar och uttag

Din bankkontoklass måste acceptera insättningar och uttag för att fungera korrekt. Nu ska vi implementera insättningar och uttag genom att skapa en journal över varje transaktion för kontot. Spårning av varje transaktion har några fördelar jämfört med att bara uppdatera saldot för varje transaktion. Historiken kan användas för att granska alla transaktioner och hantera dagliga saldon. Beräkning av saldot från historiken för alla transaktioner vid behov säkerställer att eventuella fel i en enskild transaktion som är fasta återspeglas korrekt i saldot vid nästa beräkning.

Vi börjar med att skapa en ny typ som representerar en transaktion. Transaktionen är en enkel typ som inte har något ansvar. Den behöver några egenskaper. Skapa en ny fil med namnet Transaction.cs. Lägg till följande kod i den:

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;
    }
}

Nu ska vi lägga till ett List<T> objekt Transaction i BankAccount klassen. Lägg till följande deklaration efter konstruktorn i filen BankAccount.cs :

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

Nu ska vi beräkna Balance. Det aktuella saldot kan hittas genom att summera värdena för alla transaktioner. Som koden är för närvarande kan du bara få det ursprungliga saldot för kontot, så du måste uppdatera Balance egenskapen. Ersätt raden public decimal Balance { get; } i BankAccount.cs med följande kod:

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

        return balance;
    }
}

Det här exemplet visar en viktig aspekt av egenskaper. Nu beräknar du balansen när en annan programmerare frågar efter värdet. Din beräkning räknar upp alla transaktioner och ger summan som aktuellt saldo.

Implementera sedan MakeDeposit metoderna och MakeWithdrawal . Dessa metoder tillämpar de två sista reglerna: den inledande balansen måste vara positiv och ett uttag får inte skapa en negativ balans.

Dessa regler introducerar begreppet undantag. Standardmetoden för att ange att en metod inte kan slutföra sitt arbete är att utlösa ett undantag. Typen av undantag och det meddelande som är associerat med det beskriver felet. MakeDeposit Här utlöser metoden ett undantag om beloppet för depositionen inte är större än 0. Metoden MakeWithdrawal utlöser ett undantag om uttagsbeloppet inte är större än 0, eller om tillämpningen av uttagen resulterar i ett negativt saldo. Lägg till följande kod efter listans _allTransactions deklaration:

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);
}

-instruktionen throwutlöser ett undantag. Körningen av det aktuella blocket slutar och styr överföringar till det första matchande catch blocket som finns i anropsstacken. Du lägger till ett catch block för att testa den här koden lite senare.

Konstruktorn bör få en ändring så att den lägger till en inledande transaktion i stället för att uppdatera saldot direkt. Eftersom du redan har skrivit metoden anropar du den MakeDeposit från konstruktorn. Den färdiga konstruktorn bör se ut så här:

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

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

DateTime.Now är en egenskap som returnerar aktuellt datum och tid. Testa den här koden genom att lägga till några insättningar och uttag i din Main metod genom att följa koden som skapar en ny BankAccount:

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

Testa sedan att du får feltillstånd genom att försöka skapa ett konto med ett negativt saldo. Lägg till följande kod efter föregående kod som du precis lade till:

// 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;
}

Du använder -instruktionen try-catch för att markera ett kodblock som kan utlösa undantag och för att fånga upp de fel som du förväntar dig. Du kan använda samma teknik för att testa koden som utlöser ett undantag för ett negativt saldo. Lägg till följande kod före deklarationen av invalidAccount i din Main metod:

// 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());
}

Spara filen och skriv dotnet run för att prova den.

Utmaning – logga alla transaktioner

För att slutföra den här självstudien kan du skriva metoden GetAccountHistory som skapar en string för transaktionshistoriken. Lägg till den här metoden i typen BankAccount :

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();
}

Historiken StringBuilder använder klassen för att formatera en sträng som innehåller en rad för varje transaktion. Du har sett strängformateringskoden tidigare i de här självstudierna. Ett nytt tecken är \t. Det infogar en flik för att formatera utdata.

Lägg till den här raden för att testa den i Program.cs:

Console.WriteLine(account.GetAccountHistory());

Kör programmet för att se resultatet.

Nästa steg

Om du fastnade kan du se källan för den här självstudien i vår GitHub-lagringsplats.

Du kan fortsätta med den objektorienterade programmeringskursen.

Du kan lära dig mer om dessa begrepp i dessa artiklar: