Propriedades (Guia de Programação em C#)

Uma propriedade é um membro que oferece um mecanismo flexível para ler, gravar ou calcular o valor de um campo particular. As propriedades podem ser usadas como se fossem membros de dados públicos, mas são métodos realmente especiais chamados acessadores. Esse recurso permite que os dados sejam acessados facilmente e ainda ajuda a promover a segurança e a flexibilidade dos métodos.

Visão geral das propriedades

  • As propriedades permitem que uma classe exponha uma forma pública de obter e definir valores, enquanto oculta o código de implementação ou de verificação.
  • Um acessador de propriedade get é usado para retornar o valor da propriedade e um acessador de propriedade set é usado para atribuir um novo valor. Um acessador de propriedade init é usado para atribuir um novo valor somente durante a construção de objeto. Esses acessadores podem ter diferentes níveis de acesso. Para obter mais informações, consulte Restringindo a acessibilidade aos acessadores.
  • A palavra-chave value é usada para definir o valor que está sendo atribuído pelo acessador set ou init.
  • As propriedades podem ser de leitura/gravação (elas têm um acessador get e set), somente leitura (elas têm um acessador get, mas nenhum set) ou somente gravação (elas têm um acessador set, mas nenhum get). As propriedades somente gravação são raras e são mais comumente usadas para restringir o acesso a dados confidenciais.
  • As propriedades simples que não exigem nenhum código de acessador personalizado podem ser implementadas como definições de corpo da expressão ou como propriedades autoimplementadas.

Propriedades com campos de suporte

Um padrão básico para implementar uma propriedade envolve o uso de um campo de suporte particular da propriedade para definir e recuperar o valor da propriedade. O acessador get retorna o valor do campo particular e o acessador set pode realizar alguma validação de dados antes de atribuir um valor ao campo particular. Os dois acessadores também podem realizar alguma conversão ou cálculo nos dados antes de eles serem armazenados ou retornados.

O exemplo a seguir ilustra esse padrão. Neste exemplo, a classe TimePeriod representa um intervalo de tempo. Internamente, a classe armazena o intervalo de tempo em segundos em um campo particular chamado _seconds. Uma propriedade de leitura/gravação chamada Hours permite que o cliente especifique o intervalo de tempo em horas. Tanto o acessador get quanto o set executam a conversão necessária entre horas e segundos. Além disso, o acessador set valida os dados e gera um ArgumentOutOfRangeException se o número de horas é inválido.

public class TimePeriod
{
    private double _seconds;

    public double Hours
    {
        get { return _seconds / 3600; }
        set
        {
            if (value < 0 || value > 24)
                throw new ArgumentOutOfRangeException(nameof(value),
                      "The valid range is between 0 and 24.");

            _seconds = value * 3600;
        }
    }
}

Você pode acessar propriedades para obter e definir o valor, conforme mostrado no seguinte exemplo:

TimePeriod t = new TimePeriod();
// The property assignment causes the 'set' accessor to be called.
t.Hours = 24;

// Retrieving the property causes the 'get' accessor to be called.
Console.WriteLine($"Time in hours: {t.Hours}");
// The example displays the following output:
//    Time in hours: 24

Definições de corpo de expressão

Os acessadores de propriedade geralmente consistem em instruções de linha única que simplesmente atribuem ou retornam o resultado de uma expressão. Você pode implementar essas propriedades como membros aptos para expressão. As definições de corpo da expressão consistem no símbolo => seguido pela expressão à qual atribuir ou recuperar da propriedade.

Propriedades somente leitura podem implementar o acessador get como um membro apto para expressão. Nesse caso, nem a palavra-chave do acessador get nem a palavra-chave return é usada. O exemplo a seguir implementa a propriedade Name somente leitura como um membro apto para expressão.

public class Person
{
    private string _firstName;
    private string _lastName;

    public Person(string first, string last)
    {
        _firstName = first;
        _lastName = last;
    }

    public string Name => $"{_firstName} {_lastName}";
}

Os acessadores get e set podem ser implementados como membros aptos para expressão. Nesse caso, as palavras-chave get e set devem estar presentes. O exemplo a seguir ilustra o uso de definições de corpo de expressão para ambos os acessadores. A palavra-chave return não é usada com o acessador get.

public class SaleItem
{
    string _name;
    decimal _cost;

    public SaleItem(string name, decimal cost)
    {
        _name = name;
        _cost = cost;
    }

    public string Name
    {
        get => _name;
        set => _name = value;
    }

    public decimal Price
    {
        get => _cost;
        set => _cost = value;
    }
}

Propriedades autoimplementadas

Em alguns casos, os acessadores get e set da propriedade apenas atribuem um valor ou recuperam um valor de um campo de suporte sem incluir nenhuma lógica extra. Usando propriedades autoimplementadas, você pode simplificar o código enquanto o compilador C# fornece de forma transparente o campo de suporte para você.

Se uma propriedade tiver tanto os acessadores get e set (ou get e init), ambos deverão ser autoimplementados. Você define uma propriedade autoimplementada usando as palavras-chave get e set sem fornecer qualquer implementação. O exemplo a seguir repete o anterior, exceto que Name e Price são propriedades autoimplementadas. O exemplo também remove o construtor parametrizado, de para que objetos SaleItem agora sejam inicializados com uma chamada para o construtor sem parâmetros e um inicializador de objeto.

public class SaleItem
{
    public string Name
    { get; set; }

    public decimal Price
    { get; set; }
}

As propriedades implementadas automaticamente podem declarar diferentes acessibilidades para os acessadores get e set. Normalmente, você declara um acessador público get e um acessador privado set. Saiba mais no artigo sobre como restringir a acessibilidade do acessador.

Propriedades obrigatórias

Do C# 11 em diante, você pode adicionar o membro required para forçar o código do cliente a inicializar qualquer propriedade ou campo:

public class SaleItem
{
    public required string Name
    { get; set; }

    public required decimal Price
    { get; set; }
}

Para criar umSaleItem, você precisa definir as propriedades Name e Price usando inicializadores de objeto, conforme mostrado no seguinte código:

var item = new SaleItem { Name = "Shoes", Price = 19.95m };
Console.WriteLine($"{item.Name}: sells for {item.Price:C2}");

Especificação da Linguagem C#

Para obter mais informações, veja Propriedades na Especificação da Linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Confira também