Menu

Classe e objeto em C#: campos, métodos, new e referências

Como declarar uma classe em C#, dar a ela campos e métodos, criar objetos com new e o que significa uma classe ser um tipo de referência: objetos compartilhados, null e NullReferenceException, ToString e inicializadores de objeto.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Uma classe é um tipo que você define: ela agrupa dados (campos) com o código que trabalha sobre esses dados (métodos). Um objeto é uma instância de uma classe, criada em tempo de execução com new. Uma classe, muitos objetos, cada um com seus próprios valores.

Declarando uma classe e criando objetos

A declaração de uma classe lista os membros dentro de chaves. Os campos guardam os dados, os métodos guardam o comportamento. new ClassName() aloca um objeto e dá a você uma referência para ele.

Saída:

Maya: 120.50
Omar: 40
Withdraw 200 succeeded: False

Cada objeto tem seu próprio Owner e Balance. Dentro de um método, Balance significa "o saldo do objeto em que este método foi chamado", e é por isso que maya.Deposit e omar.Deposit mudam números diferentes. Esse objeto implícito está disponível como this quando você precisa nomeá-lo.

Os campos que você não atribui começam com o valor padrão: 0 para números, false para bool e null para strings e outros tipos de classe.

Os membros aqui são public para que o Main consiga alcançá-los. Classes reais normalmente mantêm os campos private e expõem propriedades, e a página de modificadores de acesso tem as regras. Uma classe também pode declarar um construtor para definir seus campos quando o objeto é criado, assunto da página de construtores.

Uma classe é um tipo de referência

Uma variável de um tipo de classe não contém o objeto. Ela contém uma referência para um objeto que vive no heap. Atribuir a variável copia a referência, então duas variáveis podem apontar para o mesmo objeto.

Saída:

5
6
True
False

Três consequências aparecem aqui:

  • Mudar o objeto por meio de second fica visível por meio de first, porque só existe um objeto.
  • Passar um objeto para um método passa a referência. O método pode mudar os campos do objeto e quem chamou vê a mudança. (Atribuir um objeto novo ao parâmetro dentro do método não afeta a variável de quem chamou; para isso é preciso ref.)
  • == entre duas instâncias de classe compara referências por padrão: "é o mesmo objeto?", não "eles têm os mesmos valores?". lookalike tem dados idênticos e ainda assim é outro objeto.

Se você quer cópias na atribuição e comparação por valor, veja as structs, que são tipos de valor, ou os records, que comparam por valor.

null e NullReferenceException

Uma variável de referência pode guardar null, que significa "nenhum objeto". Usar um membro por meio de null lança NullReferenceException, a exceção de runtime mais comum em código C#.

Saída:

True
no city on file
NullReferenceException: Address was never set
Lisbon

A solução quase nunca é capturar a exceção. Descubra por que a referência é null e crie o objeto antes, verifique antes do uso, ou use o operador condicional nulo ?., que para e resulta em null em vez de lançar exceção. ?? então fornece um valor alternativo. Os dois operadores são cobertos em tipos anuláveis.

Sobrescrevendo o ToString

Toda classe herda de object, que fornece ToString(). A versão herdada retorna o nome completo do tipo (incluindo o namespace, se houver), o que raramente é o que você quer imprimir. Sobrescreva-o:

Saída:

Plain
Mug (8.50)
In cart: Mug (8.50)
Total line: Mug (8.50)

Console.WriteLine, a interpolação de strings e o + com uma string chamam ToString() por você. O depurador também o mostra, o que torna a sobrescrita útil mesmo quando você nunca imprime o objeto.

Inicializadores de objeto

Um inicializador de objeto define campos e propriedades públicos logo depois que o objeto é construído, na mesma expressão. Ele se lê melhor que uma lista de atribuições e funciona bem dentro de literais de coleção.

Saída:

A12: 25 (0 extras)
A13: 40 (2 extras)

Dois detalhes no segundo ingresso. Price = 40m sobrescreve o 25m do inicializador do campo, porque o inicializador de objeto executa depois que o objeto é construído. Extras = { "Parking", "Poster" } não tem new: ele chama Add na lista que o campo já guarda, em vez de substituí-la.

Um inicializador de objeto não é um construtor. Ele só pode definir membros acessíveis a partir do código que chama, e não consegue garantir que um membro seja definido. Quando um objeto é inválido sem certos valores, exija-os em um construtor.

Classes parciais

A palavra-chave partial divide uma classe em vários arquivos. O compilador junta as partes em uma única classe.

// Invoice.cs
public partial class Invoice
{
    public decimal Subtotal;
    public decimal Total() => Subtotal + Tax();
}

// Invoice.Tax.cs
public partial class Invoice
{
    private decimal Tax() => Subtotal * 0.2m;
}

Toda parte precisa dizer partial, estar no mesmo namespace e no mesmo projeto. O uso comum é código gerado: designers do WinForms, source generators e alguns ORMs escrevem uma parte e deixam a outra para você, então gerar de novo nunca sobrescreve o seu código. Dividir uma classe escrita à mão em vários arquivos só para deixá-la mais curta normalmente esconde uma classe que deveria ser duas.

Erros comuns

  • Usar um objeto antes de criá-lo. Um campo de tipo de classe é null até você atribuir new ... a ele. O primeiro acesso a um membro lança NullReferenceException.
  • Esperar que a atribuição copie. var b = a; para uma classe dá um segundo nome ao mesmo objeto. Crie um objeto novo e copie os valores quando precisar de um independente.
  • Comparar objetos com == esperando comparação por valor. Para classes, ele compara referências, a menos que a classe sobrecarregue == (como string faz). Compare os campos relevantes, ou sobrescreva Equals.
  • Imprimir um objeto e receber o nome do tipo. Sobrescreva ToString().
  • Declarar tudo como public. Funciona em exemplos pequenos, mas deixa qualquer código colocar o objeto em um estado inválido, por exemplo um saldo negativo. Mantenha os campos privados e altere-os por métodos que verificam as regras.

Perguntas frequentes

O que é uma classe em C#?

Uma classe é um tipo que você define: um grupo com nome de dados (campos e propriedades) e comportamento (métodos). class Account { public decimal Balance; public void Deposit(decimal amount) { Balance += amount; } } declara uma. Uma classe é um modelo; ela não faz nada até você criar objetos a partir dela com new.

Qual a diferença entre uma classe e um objeto em C#?

A classe é a definição, escrita uma vez no código-fonte. Um objeto é uma instância dela, criada em tempo de execução com new, com sua própria cópia de cada campo de instância. new Account() duas vezes dá dois objetos da mesma classe com saldos independentes.

Como criar um objeto em C#?

Use new seguido do nome da classe e dos argumentos do construtor: var account = new Account();. Você pode definir campos e propriedades públicos na mesma expressão com um inicializador de objeto: new Account { Owner = "Maya" }. A variável guarda uma referência para o novo objeto, não o objeto em si.

Por que recebo uma NullReferenceException em C#?

Você usou um membro (.Name, .Length, uma chamada de método) por meio de uma variável que guarda null, então não há objeto sobre o qual agir. Causas comuns são um campo que nunca foi atribuído (campos de classe começam como null), uma consulta que retornou null ou um método que retornou null em caso de falha. Verifique com if (x != null), use x?.Name, ou garanta que o objeto seja criado antes do uso.

Como sobrescrever o ToString em C#?

Declare public override string ToString() na sua classe e retorne o texto que você quer, por exemplo public override string ToString() => $"{Name} ({Price:F2})";. Sem isso, Console.WriteLine(obj) e a interpolação de strings imprimem o nome da classe, porque é isso que object.ToString() retorna.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR