Criar construtores de classe e instanciar objetos

Concluído

Além de propriedades de classe e métodos, as definições de classe incluem construtores que são usados para inicializar novos objetos (instâncias de classe).

Construtores de classe

Um construtor de classe é um método com o mesmo nome que seu tipo (métodos de construtor usam o mesmo nome que a classe).

Existem dois tipos de construtores de classe:

  • Construtores de instância. Os construtores de instância são usados para criar e inicializar quaisquer variáveis de campo de instância quando um objeto é criado.
  • Construtores estáticos. Os construtores estáticos são usados para inicializar quaisquer dados estáticos ou para executar uma ação específica que precisa ser executada apenas uma vez. Os construtores estáticos são chamados automaticamente antes que a primeira instância seja criada ou quaisquer membros estáticos sejam referenciados.

Os construtores de classe são construtores de instância por padrão.

Sintaxe do construtor de instância

Um construtor de instância é declarado usando o mesmo nome que a classe e não inclui um tipo de retorno. A assinatura do método do construtor pode incluir um modificador de acesso opcional, o nome do método e sua lista de parâmetros. As assinaturas de método de um construtor não incluem um tipo de retorno.

O exemplo a seguir mostra um construtor simples para uma classe chamada Person:


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }

    // Remaining implementation of Person class.
}

As classes podem ter mais de um construtor. Quando uma classe tem mais de um construtor, os construtores geralmente usam argumentos diferentes.

O exemplo a seguir mostra uma classe chamada Person com dois construtores.


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.
        string name = "Person One";
        Console.WriteLine($"Person created: {name}");

    }

    public Person(string fName, string lName)
    {
        string name = fName + " " + lName;

        Console.WriteLine($"Person created: {name}");
   }

   // Remaining implementation of Person class.
}

Instanciar objetos usando construtores de classe

Quando um objeto é instanciado usando a palavra-chave new, o tempo de execução do .NET chama o construtor de instância associado na definição de classe e aloca memória para o objeto.

No trecho de código a seguir, a classe Person define um construtor de instância simples. A classe Program inclui um método Main que usa o operador new para criar uma instância de Person chamada person1. O tempo de execução invoca o construtor Person imediatamente após a memória ser alocada para o novo objeto.


public class Person
{
    public Person()
    {
        // Field initialization and constructor logic goes here.

    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
    }
}

Construtores com e sem parâmetros

Um construtor que não usa parâmetros é chamado de construtor sem parâmetros. O tempo de execução invoca o construtor sem parâmetros quando um objeto é instanciado usando o operador new e nenhum argumento é fornecido ao construtor.

Observação

A menos que a classe seja estática, as classes sem construtores recebem um construtor sem parâmetros público pelo compilador C# para habilitar a instanciação de classe.

As classes geralmente definem construtores que usam parâmetros. Os construtores que usam parâmetros devem ser chamados usando o operador new ou uma instrução base. As classes podem definir um ou mais construtores.

O trecho de código a seguir mostra uma classe chamada Person com três construtores:


public class Person
{

    public Person()
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine("An instance of the Person class is being instantiated without name or age parameters.");
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using a name ({name}) parameter.");
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.

        Console.WriteLine($"An instance of the Person class is being instantiated using name ({name}) and age ({age}) parameters.");
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Person Two");
        Person person3 = new Person("Person Three", 30);
    }
}

Classes sem construtores

Se uma classe não tiver construtores de instância explícitos, o C# fornecerá um construtor sem parâmetros que você pode usar para instanciar uma instância dessa classe, como mostra o exemplo a seguir:


public class Person
{
    public int age;
    public string name = "unknown";
}

class Example
{
    static void Main()
    {
        var person = new Person();
        Console.WriteLine($"Name: {person.name}, Age: {person.age}");
        // Output:  Name: unknown, Age: 0
    }
}

Este construtor inicializa campos de instância e propriedades de acordo com os inicializadores correspondentes. Se um campo ou propriedade não tiver inicializador, seu valor será definido como o valor padrão do tipo de campo ou propriedade. Se você declarar pelo menos um construtor de instância em uma classe, C# não fornecerá um construtor sem parâmetros.

Inicializar dados de classe usando parâmetros do construtor

Os parâmetros passados para um construtor são locais para o construtor. Os parâmetros geralmente são usados para inicializar os campos de dados de uma classe.

O trecho de código a seguir mostra uma classe chamada Person com construtores que inicializam os campos personName e personAge:


public class Person
{
    public string personName;
    public string personAge;

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = "unknown";
        personAge = "unknown";
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = "unknown";
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

static class Program
{
    // the Main method is the entry point of the program.
    static void Main()
    {
        Person person1 = new Person();
        Person person2 = new Person("Person Two");
        Person person3 = new Person("Person Three", 30);

        Console.WriteLine($"Person 1 Name: {person1.personName} Age: {person1.personAge}");
        Console.WriteLine($"Person 2 Name: {person2.personName} Age: {person2.personAge}");
        Console.WriteLine($"Person 3 Name: {person3.personName} Age: {person3.personAge}");
    }
}

No exemplo anterior, a classe Person é definida com três construtores. O primeiro construtor inicializa os campos personName e personAge para "unknown". O segundo construtor inicializa o campo personName para o valor passado no parâmetro name e o campo personAge para "unknown". O terceiro construtor inicializa os campos personName e personAge para os valores passados nos parâmetros name e age, respectivamente.

Como os campos são públicos, eles podem ser acessados diretamente do método Main. Quando o código é executado, a seguinte saída é gerada:


Person 1 Name: unknown Age: unknown
Person 2 Name: Person Two Age: unknown
Person 3 Name: Person Three Age: 30

Definições do corpo da expressão

Se um construtor pode ser implementado como uma única instrução, você pode usar um expression body definition para atribuir um parâmetro a um membro da classe quando o construtor é implementado.

Por exemplo, o construtor a seguir inicializa o campo modelName com o valor passado para o parâmetro model:


public class Car
{
    public string modelName;

    public Car(string model) => modelName = model;
    
}

A classe Car tem um único campo público, modelName, que é do tipo string. O campo modelName destina-se a armazenar o nome do modelo do carro.

A classe Car também inclui um construtor que usa um único parâmetro string chamado model. O construtor usa uma definição de corpo de expressão (indicada pela sintaxe =>) para inicializar o campo modelName com o valor passado para o parâmetro model. Isso significa que quando um novo objeto Car é instanciado, o campo modelName é definido como o valor fornecido como um argumento para o construtor.

Como o termo expressão implica, o lado direito do operador => é uma expressão e não se limita a uma simples instrução de atribuição. A expressão pode ser qualquer expressão C# válida que retorna um valor.

O trecho de código a seguir demonstra como implementar uma definição de corpo de expressão que executa um cálculo simples:


public class Employee
{
    public int Salary;

    public Employee() { }

    public Employee(int annualSalary) => Salary = annualSalary;

    public Employee(int weeklySalary, int numberOfWeeks) => Salary = weeklySalary * numberOfWeeks;
}

Essa classe pode ser criada usando uma das seguintes instruções:


Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);

Construtores estáticos

Um construtor estático é usado para inicializar quaisquer dados estáticos ou para executar uma ação específica que precisa ser executada apenas uma vez. Ele é chamado automaticamente antes que a primeira instância seja criada ou quaisquer membros estáticos sejam referenciados. Um construtor estático é chamado no máximo uma vez.

O trecho de código a seguir mostra uma versão atualizada da classe Person que implementa campos estáticos e um construtor estático:


public class Person
{
    public string personName;
    public string personAge;

    // Static field
    public static string defaultName;
    public static string defaultAge;

    // Static constructor
    static Person()
    {
        // Static field initialization
        defaultName = "unknown";
        defaultAge = "unknown";
    }

    public Person()
    {
        // Field initialization and constructor logic goes here.
        personName = defaultName;
        personAge = defaultAge;
    }

    public Person(string name)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = defaultAge;
    }

    public Person(string name, int age)
    {
        // Field initialization and constructor logic goes here.
        personName = name;
        personAge = age.ToString();
    }
}

A classe Person atualizada tem dois campos de instância, personName e personAge, ambos do tipo string. Esses campos armazenam o nome e a idade de uma pessoa, respectivamente.

A classe também define dois campos estáticos, defaultName e defaultAge, também do tipo string. Os campos estáticos são compartilhados entre todas as instâncias da classe e são inicializados apenas uma vez. Nesse caso, os campos estáticos são usados para fornecer valores padrão para os campos personName e personAge.

O construtor estático static Person() é responsável por inicializar os campos estáticos. Ele define defaultName para "unknown" e defaultAge para "unknown". O construtor estático é chamado automaticamente antes que quaisquer instâncias da classe sejam criadas ou quaisquer membros estáticos sejam acessados.

A classe Person inclui três construtores de instância:

O construtor sem parâmetros public Person() inicializa os campos personName e personAge com os valores dos campos estáticos defaultName e defaultAge. Isso significa que, se nenhum argumento for fornecido ao criar um objeto Person, os valores padrão "unknown" serão usados para o nome e a idade.

O construtor public Person(string name) usa um único parâmetro, namee inicializa o campo personName com esse valor. O campo personAge é inicializado com o valor do campo estático defaultAge. Este construtor permite a criação de um objeto Person com um nome especificado enquanto usa a idade padrão.

O construtor public Person(string name, int age) usa dois parâmetros, name e age. Ele inicializa o campo personName com o valor do parâmetro name e o campo personAge com a representação de cadeia de caracteres do parâmetro age. Este construtor permite a criação de um objeto Person com um name especificado e age.

Propriedades de construtores estáticos

Os construtores estáticos têm as seguintes propriedades:

  • Um construtor estático não usa modificadores de acesso ou tem parâmetros.

  • Uma classe só pode ter um construtor estático.

  • Os construtores estáticos não podem ser herdados ou sobrecarregados.

  • Um construtor estático não pode ser chamado diretamente e destina-se apenas a ser chamado pelo Common Language Runtime (CLR). É invocado automaticamente.

  • O usuário não tem controle sobre quando o construtor estático é executado no programa.

  • Um construtor estático é chamado automaticamente. Ele inicializa a classe antes que a primeira instância seja criada ou quaisquer membros estáticos declarados nessa classe (não suas classes base) sejam referenciados. Um construtor estático é executado antes de um construtor de instância. Se inicializadores de variáveis de campo estático estiverem presentes na classe do construtor estático, eles serão executados na ordem textual em que aparecem na declaração de classe. Os inicializadores são executados imediatamente antes do construtor estático.

  • Se você não fornecer um construtor estático para inicializar campos estáticos, todos os campos estáticos serão inicializados com seu valor padrão.

  • Se um construtor estático lançar uma exceção, o tempo de execução não a invocará uma segunda vez e o tipo permanecerá não inicializado durante o tempo de vida do domínio do aplicativo. Mais comumente, uma exceção TypeInitializationException é lançada quando um construtor estático é incapaz de instanciar um tipo ou para uma exceção não tratada que ocorre dentro de um construtor estático. Para construtores estáticos que não estão explicitamente definidos no código-fonte, a solução de problemas pode exigir a inspeção do código de linguagem intermediária (IL).

  • A presença de um construtor estático impede a adição do atributo BeforeFieldInit type. Isso limita a otimização do tempo de execução.

  • Um campo declarado como static readonly só pode ser atribuído como parte de sua declaração ou em um construtor estático. Quando um construtor estático explícito não for necessário, inicialize campos estáticos na declaração em vez de através de um construtor estático para melhor otimização do tempo de execução.

  • O tempo de execução chama um construtor estático não mais do que uma vez em um único domínio de aplicativo. Essa chamada é feita em uma região bloqueada com base no tipo específico da classe. Não são necessários mecanismos de bloqueio adicionais no corpo de um construtor estático.

Observação

Embora não seja diretamente acessível, a presença de um construtor estático explícito deve ser documentada para ajudar na solução de problemas de exceções de inicialização.