Partilhar via


Explore a programação orientada a objetos com classes e objetos

Neste tutorial, você criará um aplicativo de console e verá os recursos básicos orientados a objetos que fazem parte da linguagem C#.

Pré-requisitos

Criar a sua aplicação

Usando uma janela de terminal, crie um diretório chamado Classes. Você criará seu aplicativo lá. Mude para esse diretório e digite dotnet new console na janela do console. Este comando cria seu aplicativo. Abra o Program.cs. Deverá ter o seguinte aspeto:

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

Neste tutorial, você criará novos tipos que representam uma conta bancária. Normalmente, os desenvolvedores definem cada classe em um arquivo de texto diferente. Isso facilita o gerenciamento à medida que um programa cresce em tamanho. Crie um novo arquivo chamado BankAccount.cs no diretório Classes .

Este ficheiro conterá a definição de uma conta bancária. A programação orientada a objetos organiza o código criando tipos na forma de classes. Essas classes contêm o código que representa uma entidade específica. A BankAccount classe representa uma conta bancária. O código implementa operações específicas através de métodos e propriedades. Neste tutorial, a conta bancária suporta este comportamento:

  1. Tem um número de 10 dígitos que identifica exclusivamente a conta bancária.
  2. Ele tem uma cadeia de caracteres que armazena o nome ou nomes dos proprietários.
  3. O saldo pode ser recuperado.
  4. Aceita depósitos.
  5. Aceita levantamentos.
  6. O saldo inicial deve ser positivo.
  7. Os levantamentos não podem resultar num saldo negativo.

Definir o tipo de conta bancária

Você pode começar criando as noções básicas de uma classe que define esse comportamento. Crie um novo arquivo usando o comando File:New . Nomeie-o BankAccount.cs. Adicione o seguinte código ao seu arquivo 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)
    {
    }
}

Antes de continuar, vamos dar uma olhada no que você construiu. A namespace declaração fornece uma maneira de organizar logicamente seu código. Este tutorial é relativamente pequeno, então você colocará todo o código em um namespace.

public class BankAccount define a classe, ou tipo, que você está criando. Tudo dentro do { e } que segue a declaração de classe define o estado e o comportamento da classe. Há cinco membros da BankAccount turma. Os três primeiros são propriedades. As propriedades são elementos de dados e podem ter código que impõe a validação ou outras regras. Os dois últimos são métodos. Os métodos são blocos de código que executam uma única função. A leitura dos nomes de cada um dos membros deve fornecer informações suficientes para você ou outro desenvolvedor entender o que a classe faz.

Abrir uma nova conta

O primeiro recurso a implementar é abrir uma conta bancária. Quando um cliente abre uma conta, ele deve fornecer um saldo inicial e informações sobre o proprietário ou proprietários dessa conta.

Criar um novo objeto do BankAccount tipo significa definir um construtor que atribui esses valores. Um construtor é um membro que tem o mesmo nome que a classe. Ele é usado para inicializar objetos desse tipo de classe. Adicione o seguinte construtor ao BankAccount tipo. Colocar o seguinte código acima da declaração de MakeDeposit:

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

O código anterior identifica as propriedades do objeto que está sendo construído incluindo o this qualificador. Esse qualificador é geralmente opcional e omitido. Você também poderia ter escrito:

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

O this qualificador só é necessário quando uma variável ou parâmetro local tem o mesmo nome que esse campo ou propriedade. O this qualificador é omitido ao longo do restante deste artigo, a menos que seja necessário.

Os construtores são chamados quando você cria um objeto usando newo . Substitua a linha Console.WriteLine("Hello World!"); no Program.cs pelo seguinte código (substitua <name> pelo seu nome):

using Classes;

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

Vamos executar o que você construiu até agora. Se você estiver usando o Visual Studio, selecione Iniciar sem depuração no menu Depurar . Se você estiver usando uma linha de comando, digite dotnet run o diretório onde você criou seu projeto.

Reparou que o número da conta está em branco? É hora de corrigir isso. O número da conta deve ser atribuído quando o objeto é construído. Mas não deve ser responsabilidade do chamador criá-lo. O BankAccount código de classe deve saber como atribuir novos números de conta. Uma maneira simples é começar com um número de 10 dígitos. Incremente-o quando cada nova conta for criada. Finalmente, armazene o número da conta atual quando um objeto é construído.

Adicione uma declaração de membro à BankAccount classe. Coloque a seguinte linha de código após a chave { de abertura no início da BankAccount classe:

private static int s_accountNumberSeed = 1234567890;

O accountNumberSeed é um membro de dados. É , o privateque significa que só pode ser acessado por código dentro da BankAccount classe. É uma forma de separar as responsabilidades públicas (como ter um número de conta) da implementação privada (como os números de conta são gerados). É também static, o que significa que é compartilhado por todos os BankAccount objetos. O valor de uma variável não estática é exclusivo para cada instância do BankAccount objeto. O accountNumberSeed é um private static campo e, portanto, tem o prefixo s_ de acordo com as convenções de nomenclatura C#. O s campo denotando static e _ denotando private . Adicione as duas linhas a seguir ao construtor para atribuir o número da conta. Coloque-os depois da linha que diz this.Balance = initialBalance:

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

Digite dotnet run para ver os resultados.

Crie depósitos e levantamentos

A classe da sua conta bancária tem de aceitar depósitos e levantamentos para funcionar corretamente. Vamos implementar depósitos e saques criando um diário de cada transação para a conta. Acompanhar cada transação tem algumas vantagens em relação à simples atualização do saldo de cada transação. O histórico pode ser usado para auditar todas as transações e gerenciar saldos diários. Calcular o saldo a partir do histórico de todas as transações, quando necessário, garante que quaisquer erros em uma única transação que sejam corrigidos serão refletidos corretamente no saldo no próximo cálculo.

Vamos começar criando um novo tipo para representar uma transação. A transação é um tipo simples que não tem quaisquer responsabilidades. Precisa de algumas propriedades. Crie um novo arquivo chamado Transaction.cs. Adicione o seguinte código:

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

Agora, vamos adicionar um List<T> dos Transaction objetos à BankAccount classe. Adicione a seguinte declaração após o construtor em seu arquivo BankAccount.cs :

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

Agora, vamos calcular corretamente o Balance. O saldo atual pode ser encontrado somando os valores de todas as transações. Como o código é atualmente, você só pode obter o saldo inicial da conta, então você terá que atualizar o Balance imóvel. Substitua a linha public decimal Balance { get; } no BankAccount.cs pelo seguinte código:

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

        return balance;
    }
}

Este exemplo mostra um aspeto importante das propriedades. Agora você está calculando o saldo quando outro programador pede o valor. Seu cálculo enumera todas as transações e fornece a soma como o saldo atual.

Em seguida, implemente os MakeDeposit métodos e MakeWithdrawal . Estes métodos aplicarão as duas últimas regras: o saldo inicial deve ser positivo e qualquer levantamento não deve criar um saldo negativo.

Estas regras introduzem o conceito de exceções. A maneira padrão de indicar que um método não pode concluir seu trabalho com êxito é lançar uma exceção. O tipo de exceção e a mensagem associada a ela descrevem o erro. Aqui, o MakeDeposit método lança uma exceção se o valor do depósito não for maior que 0. O MakeWithdrawal método lança uma exceção se o valor do saque não for maior que 0, ou se a aplicação do saque resultar em um saldo negativo. Adicione o seguinte código após a _allTransactions declaração da lista:

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

A throw declaração lança uma exceção. A execução do bloco atual termina e o controle é transferido para o primeiro bloco correspondente catch encontrado na pilha de chamadas. Você adicionará um catch bloco para testar esse código um pouco mais tarde.

O construtor deve obter uma alteração para que ele adicione uma transação inicial, em vez de atualizar o saldo diretamente. Como você já escreveu o MakeDeposit método, chame-o de seu construtor. O construtor acabado deve ter esta aparência:

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

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

DateTime.Now é uma propriedade que retorna a data e hora atuais. Teste este código adicionando alguns depósitos e levantamentos no seu Main método, seguindo o código que cria um novo 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);

Em seguida, teste se você está detetando condições de erro tentando criar uma conta com um saldo negativo. Adicione o seguinte código após o código anterior que você acabou de adicionar:

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

Você usa a try-catch instrução para marcar um bloco de código que pode gerar exceções e para capturar os erros esperados. Você pode usar a mesma técnica para testar o código que lança uma exceção para um saldo negativo. Adicione o seguinte código antes da declaração de invalidAccount no seu Main método:

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

Salve o arquivo e digite dotnet run para experimentá-lo.

Desafio - registre todas as transações

Para concluir este tutorial, você pode escrever o GetAccountHistory método que cria um string para o histórico de transações. Adicione este método ao BankAccount tipo:

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

O histórico usa a StringBuilder classe para formatar uma cadeia de caracteres que contém uma linha para cada transação. Você já viu o código de formatação da cadeia de caracteres anteriormente nestes tutoriais. Um novo personagem é \t. Isso insere uma guia para formatar a saída.

Adicione esta linha para testá-la em Program.cs:

Console.WriteLine(account.GetAccountHistory());

Execute o programa para ver os resultados.

Próximos passos

Se você ficou preso, você pode ver a fonte para este tutorial em nosso repositório GitHub.

Você pode continuar com o tutorial de programação orientada a objetos.

Você pode aprender mais sobre esses conceitos nestes artigos: