Udostępnij za pośrednictwem


Eksplorowanie programowania obiektowego za pomocą klas i obiektów

W tym samouczku utworzysz aplikację konsolową i zobaczysz podstawowe funkcje zorientowane na obiekt, które są częścią języka C#.

Wymagania wstępne

  • Najnowszy .NET SDK
  • Edytor programu Visual Studio Code
  • Zestaw deweloperski C#

Instrukcje instalacji

W systemie Windows użyj tego pliku konfiguracyjnego WinGet , aby zainstalować wszystkie wymagane komponenty wstępne. Jeśli masz już coś zainstalowanego, usługa WinGet pominie ten krok.

  1. Pobierz plik i kliknij dwukrotnie, aby go uruchomić.
  2. Przeczytaj umowę licencyjną, wpisz yi wybierz pozycję Wprowadź po wyświetleniu monitu o zaakceptowanie.
  3. Jeśli na pasku zadań zostanie wyświetlony monit kontroli konta użytkownika (UAC), zezwól na kontynuowanie instalacji.

Na innych platformach należy zainstalować każdy z tych składników oddzielnie.

  1. Pobierz zalecany instalator ze strony pobierania zestawu SDK platformy .NET i kliknij dwukrotnie, aby go uruchomić. Strona pobierania wykrywa platformę i zaleca najnowszy instalator twojej platformy.
  2. Pobierz najnowszy instalator z strony głównej programu Visual Studio Code i kliknij dwukrotnie, aby go uruchomić. Ta strona wykrywa również platformę, a link powinien być poprawny dla twojego systemu.
  3. Kliknij przycisk "Zainstaluj" na stronie rozszerzenia C# DevKit. Spowoduje to otwarcie programu Visual Studio Code i pytanie, czy chcesz zainstalować lub włączyć rozszerzenie. Wybierz pozycję "Zainstaluj".

Tworzenie aplikacji

Za pomocą okna terminalu utwórz katalog o nazwie Classes. Utworzysz tam aplikację. Przejdź do tego katalogu i wpisz dotnet new console w oknie konsoli. To polecenie tworzy aplikację. Otwórz plik Program.cs. Powinien on wyglądać następująco:

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

W tym samouczku utworzysz nowe typy reprezentujące konto bankowe. Zazwyczaj deweloperzy definiują każdą klasę w innym pliku tekstowym. Ułatwia to zarządzanie w miarę wzrostu rozmiaru programu. Utwórz nowy plik o nazwie BankAccount.cs w katalogu Classes .

Ten plik będzie zawierać definicję konta bankowego. Programowanie obiektowe organizuje kod, tworząc typy w postaci klas. Te klasy zawierają kod reprezentujący określoną jednostkę. Klasa BankAccount reprezentuje konto bankowe. Kod implementuje określone operacje za pomocą metod i właściwości. W tym samouczku konto bankowe obsługuje następujące zachowanie:

  1. Ma 10-cyfrowy numer, który jednoznacznie identyfikuje konto bankowe.
  2. Zawiera on ciąg, który przechowuje nazwę lub nazwy właścicieli.
  3. Bilans można odzyskać.
  4. Akceptuje depozyty.
  5. Akceptuje wypłaty.
  6. Początkowe saldo musi być dodatnie.
  7. Wypłaty nie mogą skutkować ujemnym saldem.

Definiowanie typu konta bankowego

Możesz zacząć od utworzenia podstaw klasy, która definiuje to zachowanie. Utwórz nowy plik przy użyciu polecenia File:New . Nadaj mu nazwę BankAccount.cs. Dodaj następujący kod do pliku BankAccount.cs :

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

Zanim przejdziemy dalej, przyjrzyjmy się temu, co stworzyłeś. Deklaracja namespace umożliwia logiczne organizowanie kodu. Ten tutorial jest stosunkowo mały, więc cały kod zostanie umieszczony w jednej przestrzeni nazwowej.

public class BankAccount definiuje klasę lub typ, który tworzysz. Wszystko wewnątrz { i }, co następuje po deklaracji klasy, definiuje stan i zachowanie klasy. Istnieje pięć członkówBankAccount klasy. Pierwsze trzy to właściwości. Właściwości to elementy danych i mogą mieć kod wymuszający walidację lub inne reguły. Ostatnie dwa są metodami. Metody to bloki kodu, które wykonują jedną funkcję. Odczytywanie nazw poszczególnych elementów członkowskich powinno dostarczyć użytkownikowi lub innemu deweloperowi wystarczające informacje, aby zrozumieć, co robi klasa.

Otwieranie nowego konta

Pierwszą funkcją implementacji jest otwarcie konta bankowego. Gdy klient otworzy konto, musi podać początkowe saldo oraz informacje o właścicielu lub właścicielach tego konta.

Utworzenie nowego obiektu BankAccount typu oznacza zdefiniowanie konstruktora, który przypisuje te wartości. Konstruktor jest elementem członkowskim o takiej samej nazwie jak klasa. Służy do inicjowania obiektów tego typu klasy. Dodaj następujący konstruktor do typu BankAccount. Umieść następujący kod powyżej deklaracji MakeDeposit:

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

Powyższy kod identyfikuje właściwości obiektu konstruowanego przez dołączenie kwalifikatora this . Ten określnik jest zwykle opcjonalny i pomijany. Można również napisać:

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

this Kwalifikator jest wymagany tylko wtedy, gdy zmienna lokalna lub parametr ma taką samą nazwę jak to pole lub właściwość. W pozostałej części tego artykułu kwalifikator this zostanie pominięty, chyba że będzie to konieczne.

Konstruktory są wywoływane podczas tworzenia obiektu przy użyciu polecenia new. Zastąp wiersz Console.WriteLine("Hello World!"); w pliku Program.cs następującym kodem (zmień <name> na swoje imię):

using Classes;

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

Uruchommy to, co już stworzyłeś. Jeśli używasz programu Visual Studio, wybierz pozycję Rozpocznij bez debugowania z menu Debugowanie . Jeśli używasz wiersza polecenia, wpisz dotnet run w katalogu, w którym utworzono projekt.

Czy zauważyłeś, że numer konta jest pusty? Nadszedł czas, aby to naprawić. Numer konta należy przypisać podczas konstruowania obiektu. Ale nie powinno to być obowiązkiem rozmówcy, aby go utworzyć. Kod BankAccount klasy powinien wiedzieć, jak przypisać nowe numery kont. Prostym sposobem jest rozpoczęcie od 10-cyfrowej liczby. Zwiększ go po utworzeniu każdego nowego konta. Na koniec zapisz numer bieżącego konta podczas konstruowania obiektu.

Dodaj deklarację składową do klasy BankAccount. Umieść następujący wiersz kodu za otwierającym nawiasem klamrowym { na początku klasy BankAccount.

private static int s_accountNumberSeed = 1234567890;

Element accountNumberSeed jest członem danych. Oznacza to, że dostęp do niego można uzyskać tylko za pomocą kodu wewnątrz klasy private. Jest to sposób oddzielenia obowiązków publicznych (takich jak posiadanie numeru konta) od implementacji prywatnej (sposób generowania numerów kont). Jest to również static, co oznacza, że jest współużytkowany we wszystkich BankAccount obiektach. Wartość zmiennej niestatycznej jest unikatowa dla każdego wystąpienia BankAccount obiektu. accountNumberSeed jest polem typu private static, a zatem posiada prefiks s_ zgodnie z konwencjami nazewnictwa języka C#. Oznaczenie s oraz static symbolizujące pole _ oraz private. Dodaj następujące dwa wiersze do konstruktora, aby przypisać numer konta. Umieść je po wierszu z napisem this.Balance = initialBalance:

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

Wpisz dotnet run , aby wyświetlić wyniki.

Tworzenie depozytów i wypłat

Klasa konta bankowego musi zaakceptować depozyty i wypłaty, aby działały prawidłowo. Zaimplementujmy depozyty i wypłaty, tworząc dziennik każdej transakcji dla konta. Śledzenie każdej transakcji ma kilka zalet w stosunku do prostego aktualizowania salda dla każdej transakcji. Historia może służyć do inspekcji wszystkich transakcji i zarządzania dziennymi saldami. Obliczenie salda z historii wszystkich transakcji w razie potrzeby gwarantuje, że wszelkie błędy w jednej transakcji, które zostały naprawione, zostaną poprawnie odzwierciedlone w saldzie przy następnym obliczeniu.

Zacznijmy od utworzenia nowego typu reprezentującego transakcję. Transakcja jest prostym typem, który nie ma żadnych obowiązków. Potrzebuje kilku właściwości. Utwórz nowy plik o nazwie Transaction.cs. Dodaj do niej następujący kod:

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

Teraz dodajmy List<T> obiekty Transaction do klasy BankAccount. Dodaj następującą deklarację w pliku BankAccount.cs po konstruktorze.

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

Teraz poprawnie obliczmy element Balance. Bieżące saldo można znaleźć, sumując wartości wszystkich transakcji. W obecnym stanie kodu możesz uzyskać tylko saldo początkowe konta, więc musisz zaktualizować Balance właściwość. Zastąp wiersz public decimal Balance { get; } w BankAccount.cs następującym kodem:

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

        return balance;
    }
}

W tym przykładzie przedstawiono ważny aspekt właściwości. Teraz obliczasz saldo, gdy inny programista pyta o wartość. Obliczenia wyliczają wszystkie transakcje i udostępniają sumę jako bieżące saldo.

Następnie zaimplementuj metody MakeDeposit i MakeWithdrawal. Te metody wymuszają dwie ostatnie reguły: początkowe saldo musi być dodatnie, a każde wycofanie nie może utworzyć ujemnego salda.

Te reguły wprowadzają pojęcie wyjątków. Standardowym sposobem wskazywania, że metoda nie może pomyślnie ukończyć swojej pracy, jest zgłoszenie wyjątku. Typ wyjątku i skojarzony z nim komunikat opisują błąd. Metoda MakeDeposit zgłasza wyjątek, jeśli kwota depozytu nie jest większa niż 0. Metoda MakeWithdrawal zgłasza wyjątek, jeśli kwota wypłaty nie jest większa niż 0, lub jeśli zastosowanie wypłaty spowoduje ujemne saldo. Dodaj następujący kod po deklaracji _allTransactions listy:

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

throw Instrukcja zgłasza wyjątek. Wykonanie bieżącego bloku kończy się, a kontrola jest przenoszona do pierwszego odpowiedniego bloku catch znalezionego w stosie wywołań. Dodasz catch blok, aby przetestować ten kod nieco później.

Konstruktor powinien wprowadzić jedną zmianę, aby dodawał początkową transakcję, zamiast bezpośredniej aktualizacji salda. Ponieważ już napisałeś metodę MakeDeposit , wywołaj ją z konstruktora. Gotowy konstruktor powinien wyglądać następująco:

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

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

DateTime.Now jest właściwością zwracającą bieżącą datę i godzinę. Przetestuj ten kod, dodając kilka depozytów i wypłat w metodzie Main, przyglądając się kodowi, który tworzy nowy 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);

Następnie przetestuj, czy przechwytujesz warunki błędu, próbując utworzyć konto z ujemnym saldem. Dodaj następujący kod po właśnie dodanym kodzie:

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

Używasz instrukcji try-catch do oznaczania bloku kodu, który może zgłaszać wyjątki, oraz do przechwytywania tych wyjątków, które się spodziewasz. Możesz użyć tej samej techniki, aby przetestować kod, który zgłasza wyjątek dla ujemnego salda. Dodaj następujący kod przed deklaracją invalidAccount w metodzie Main :

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

Zapisz plik i wpisz dotnet run , aby go wypróbować.

Wyzwanie — rejestrowanie wszystkich transakcji

Aby ukończyć ten samouczek, możesz napisać metodę GetAccountHistory, która tworzy string obiekt dla historii transakcji. Dodaj tę metodę do BankAccount typu:

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

Historia używa StringBuilder klasy do formatowania ciągu zawierającego jeden wiersz dla każdej transakcji. Kod formatowania ciągów został już wcześniej wyświetlony w tych samouczkach. Jeden nowy znak to \t. To wstawia tabulator w celu sformatowania danych wyjściowych.

Dodaj ten wiersz, aby przetestować go w Program.cs:

Console.WriteLine(account.GetAccountHistory());

Uruchom program, aby wyświetlić wyniki.

Następne kroki

Jeśli utkniesz, możesz zobaczyć źródło tego samouczka w naszym repozytorium GitHub.

Możesz kontynuować pracę z samouczkiem dotyczącym programowania obiektowego .

Więcej informacji na temat tych pojęć można uzyskać w następujących artykułach: