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

Uma propriedade é um membro que fornece um mecanismo flexível para ler, gravar ou calcular o valor de um campo de dados. As propriedades aparecem como membros de dados públicos, mas são implementadas como métodos especiais chamados acessadores. Esse recurso permite que os chamadores acessem os dados facilmente e ainda ajuda a promover a segurança e a flexibilidade dos dados. A sintaxe das propriedades é uma extensão natural dos campos. Um campo define um local de armazenamento:

public class Person
{
    public string? FirstName;

    // Omitted for brevity.
}

Propriedades implementadas automaticamente

Uma definição de propriedade contém declarações para um get e set acessador que recupera e atribui o valor dessa propriedade:

public class Person
{
    public string? FirstName { get; set; }

    // Omitted for brevity.
}

O exemplo anterior mostra uma propriedade implementada automaticamente. O compilador gera um campo de suporte oculto para a propriedade. O compilador também implementa o corpo dos get e set acessadores. Todos os atributos são aplicados à propriedade implementada automaticamente. Você pode aplicar o atributo ao campo de suporte gerado pelo compilador especificando a field: tag no atributo.

Você pode inicializar uma propriedade com um valor diferente do padrão definindo um valor após a chave de fechamento da propriedade. Você pode preferir que o valor inicial da FirstName propriedade seja a cadeia de caracteres vazia em vez de null. Você deve especificar isso como mostrado no código a seguir:

public class Person
{
    public string FirstName { get; set; } = string.Empty;

    // Omitted for brevity.
}

Controlo de acesso

Os exemplos anteriores mostraram propriedades de leitura/gravação. Você também pode criar propriedades somente leitura ou dar acessibilidade diferente ao conjunto e obter acessadores. Suponha que sua Person classe só deve habilitar a FirstName alteração do valor da propriedade de outros métodos nessa classe. Você pode dar ao acessor private definido acessibilidade em vez de public:

public class Person
{
    public string? FirstName { get; private set; }

    // Omitted for brevity.
}

A FirstName propriedade pode ser lida a partir de qualquer código, mas pode ser atribuída apenas a Person partir do código na classe.

Você pode adicionar qualquer modificador de acesso restritivo ao conjunto ou obter acessadores. Um modificador de acesso em um acessador individual deve ser mais restritivo do que o acesso da propriedade. O código anterior é legal porque a FirstName propriedade é public, mas o acessador definido é private. Não foi possível declarar um private imóvel com acessório public . As declarações de propriedade também podem ser declaradas protected, internal, protected internal, ou, mesmo private.

Existem dois modificadores de acesso especiais para set acessadores:

  • Um set acessador pode ter init como modificador de acesso. Esse set acessador pode ser chamado somente a partir de um inicializador de objeto ou dos construtores do tipo. É mais restritivo do que private no set acessório.
  • Uma propriedade implementada automaticamente pode declarar um get acessador sem um set acessador. Nesse caso, o compilador permite que o set acessador seja chamado apenas a partir dos construtores do tipo. É mais restritivo do que o init acessador no set acessador.

Modifique a Person classe da seguinte maneira:

public class Person
{
    public Person(string firstName) => FirstName = firstName;

    public string FirstName { get; }

    // Omitted for brevity.
}

O exemplo anterior requer que os chamadores usem o construtor que inclui o FirstName parâmetro. Os chamadores não podem usar inicializadores de objeto para atribuir um valor à propriedade. Para suportar inicializadores, você pode tornar o set acessador um init acessador, conforme mostrado no código a seguir:

public class Person
{
    public Person() { }
    public Person(string firstName) => FirstName = firstName;

    public string? FirstName { get; init; }

    // Omitted for brevity.
}

Esses modificadores são frequentemente usados com o modificador para forçar a required inicialização adequada.

Propriedades obrigatórias

O exemplo anterior permite que um chamador crie um Person usando o construtor padrão, sem definir a FirstName propriedade. A propriedade mudou o tipo para uma cadeia de caracteres anulável . A partir do C# 11, você pode exigir que os chamadores definam uma propriedade:

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName) => FirstName = firstName;

    public required string FirstName { get; init; }

    // Omitted for brevity.
}

O código anterior faz duas alterações na Person classe. Primeiro, a declaração de FirstName propriedade inclui o required modificador. Isso significa que qualquer código que crie um novo Person deve definir essa propriedade usando um inicializador de objeto. Em segundo lugar, o construtor que usa um firstName parâmetro tem o System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute atributo. Este atributo informa o compilador que este construtor define todos os required membros. Os chamadores que usam esse construtor não são obrigados a definir required propriedades com um inicializador de objeto.

Importante

Não confunda required com não-anulável. É válido definir uma required propriedade como null ou default. Se o tipo não for anulável, como string nesses exemplos, o compilador emitirá um aviso.

var aPerson = new Person("John");
aPerson = new Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();

Definições do corpo da expressão

Os acessadores de propriedade geralmente consistem em instruções de linha única. Os acessadores atribuem ou retornam o resultado de uma expressão. Você pode implementar essas propriedades como membros com corpo de expressão. As definições do corpo da expressão consistem no => token seguido pela expressão a ser atribuída ou recuperada da propriedade.

As propriedades somente leitura podem implementar o get acessador como um membro com corpo de expressão. O exemplo a seguir implementa a propriedade somente Name leitura como um membro com corpo de expressão:

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public required string FirstName { get; init; }
    public required string LastName { get; init; }

    public string Name => $"{FirstName} {LastName}";

    // Omitted for brevity.
}

A Name propriedade é uma propriedade calculada. Não há campo de apoio para Name. A propriedade calcula-o de cada vez.

Propriedades com campos de apoio

Você pode misturar o conceito de uma propriedade computada com um campo privado e criar uma propriedade avaliada em cache. Por exemplo, atualize a FullName propriedade para que a formatação da cadeia de caracteres aconteça no primeiro acesso:

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string firstName, string lastName)
    {
        FirstName = firstName;
        LastName = lastName;
    }

    public required string FirstName { get; init; }
    public required string LastName { get; init; }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

Essa implementação funciona porque as propriedades e LastName são FirstName somente leitura. As pessoas podem mudar de nome. A atualização das FirstName propriedades e LastName para permitir set acessadores exige que você invalide qualquer valor armazenado em cache para fullName. Você modifica os set acessadores da FirstName propriedade e LastName para que o fullName campo seja calculado novamente:

public class Person
{
    private string? _firstName;
    public string? FirstName
    {
        get => _firstName;
        set
        {
            _firstName = value;
            _fullName = null;
        }
    }

    private string? _lastName;
    public string? LastName
    {
        get => _lastName;
        set
        {
            _lastName = value;
            _fullName = null;
        }
    }

    private string? _fullName;
    public string FullName
    {
        get
        {
            if (_fullName is null)
                _fullName = $"{FirstName} {LastName}";
            return _fullName;
        }
    }
}

Esta versão final avalia a FullName propriedade apenas quando necessário. A versão calculada anteriormente é usada, se válida. Caso contrário, o cálculo atualiza o valor armazenado em cache. Os desenvolvedores que usam essa classe não precisam saber os detalhes da implementação. Nenhuma dessas alterações internas afeta o uso do objeto Person.

A partir do C# 13, você pode criar partial propriedades em partial classes. A declaração de implementação de uma partial propriedade não pode ser uma propriedade implementada automaticamente. Uma propriedade implementada automaticamente usa a mesma sintaxe que uma declaração de propriedade parcial declarada.

Propriedades

As propriedades são uma forma de campos inteligentes em uma classe ou objeto. De fora do objeto, eles aparecem como campos no objeto. No entanto, as propriedades podem ser implementadas usando a paleta completa da funcionalidade C#. Você pode fornecer validação, acessibilidade diferente, avaliação preguiçosa ou quaisquer requisitos que seus cenários precisem.

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

Especificação da linguagem C#

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

Consulte também