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. 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 med C# DevKit. 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:
- Den har ett tiosiffrigt nummer som unikt identifierar bankkontot.
- Den har en sträng som lagrar ägarnas namn eller namn.
- Saldot kan hämtas.
- Den tar emot insättningar.
- Den accepterar uttag.
- Den inledande balansen måste vara positiv.
- 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 throw
utlö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: