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 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 dados facilmente e ainda ajuda a promover a segurança e a flexibilidade dos dados. A sintaxe para propriedades é uma extensão natural para os 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 acessador get
e set
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 acessadores get
e set
. Todos os atributos são aplicados à propriedade implementada automaticamente. Você pode aplicar o atributo ao campo de suporte gerado pelo compilador especificando a tag field:
no atributo.
Você pode inicializar uma propriedade para um valor diferente do padrão definindo um valor após a chave de fechamento da propriedade. Talvez você prefira que o valor inicial para a propriedade FirstName
seja a cadeia de caracteres vazia em vez de null
. Você especificaria isso conforme mostrado no código a seguir:
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
Controle de acesso
Os exemplos anteriores mostraram propriedades de leitura/gravação. Você também pode criar propriedades somente leitura ou dar acessibilidade diferente aos acessadores get e set. Suponha que sua classe Person
só deva habilitar a alteração do valor da propriedade FirstName
em outros métodos naquela classe. Você pode dar acessibilidade private
ao acessador set, em vez de public
:
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
A propriedade FirstName
pode ser lida em qualquer código, mas só pode ser atribuída do código na classe Person
.
Você pode adicionar qualquer modificador de acesso restritivo aos acessadores get ou set. 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 propriedade FirstName
é public
, mas o acessador set é private
. Você não poderia declarar uma propriedade private
com um acessador public
. As declarações de propriedade também podem ser declaradas protected
, internal
, protected internal
ou até mesmo private
.
Há dois modificadores de acesso especiais para acessadores set
:
- Um acessador
set
pode terinit
como seu modificador de acesso. Esse acessadorset
pode ser chamado somente de um inicializador de objeto ou dos construtores do tipo. É mais restritivo do queprivate
no acessadorset
. - Uma propriedade implementada automaticamente pode declarar um
get
acessador sem umset
acessador. Nesse caso, o compilador permite que o acessadorset
seja chamado somente dos construtores do tipo. É mais restritivo do que o acessadorinit
no acessadorset
.
Modifique a classe Person
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 parâmetro FirstName
. Os chamadores não podem usar inicializadores de objeto para atribuir um valor à propriedade. Para dar suporte a inicializadores, você pode transformar o set
em um init
, conforme mostrado no seguinte código:
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 required
para forçar a inicialização adequada.
Propriedades obrigatórias
O exemplo anterior permite que um chamador crie um Person
usando o construtor padrão, sem definir a propriedade FirstName
. A propriedade alterou o tipo para uma cadeia de caracteres que permite valor nulo. 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 classe Person
. Primeiro, a declaração de propriedade FirstName
inclui o modificador required
. 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 parâmetro firstName
tem o atributo System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute. Esse atributo informa ao compilador que esse construtor define todos required
os membros. Os chamadores que usam esse construtor não precisam definir propriedades required
com um inicializador de objeto.
Importante
Não confunda required
com não anulável. É válido definir uma propriedade required
como null
ou default
. Se o tipo for não 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 de corpo de 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 aptos para expressão. As definições de corpo da expressão consistem no token =>
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. O exemplo a seguir implementa a propriedade Name
somente leitura como um membro apto para 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 propriedade Name
é uma propriedade computada. Não há campo de apoio para Name
. A propriedade calcula isso toda vez.
Propriedades com campos de suporte
Combine o conceito de uma propriedade computada com um campo privado e crie uma propriedade avaliada armazenada em cache. Por exemplo, atualize a propriedade FullName
para que a formatação da string ocorra 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 FirstName
e LastName
são somente leitura. As pessoas podem mudar o nome. A atualização das propriedades FirstName
e LastName
para permitir acessadores set
exige que você invalide qualquer valor armazenado em cache para fullName
. Modifique os acessadores set
das propriedades FirstName
e LastName
para que o campo fullName
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 propriedade FullName
apenas quando necessário. Se a versão calculada anteriormente é válida, ela é usada. Caso contrário, o cálculo atualizará o valor armazenado em cache. Os desenvolvedores que usam essa classe não precisam saber dos detalhes da implementação. Nenhuma dessas alterações internas afetam o uso do objeto Person.
A partir do C# 13, você pode criar partial
propriedades em classes partial
. 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 de declaração.
Propriedades
As propriedades são uma forma de campos inteligentes em uma classe ou objeto. De fora do objeto, elas parecem como campos no objeto. No entanto, as propriedades podem ser implementadas usando a paleta completa de funcionalidades do C#. Você pode fornecer validação, acessibilidade diferente, avaliação lenta ou quaisquer requisitos necessários aos seus cenários.
- Propriedades simples que não exigem nenhum 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 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 o acessor
set
ouinit
está atribuindo. - As propriedades podem ser de leitura/gravação (elas têm um acessador
get
eset
), somente leitura (elas têm um acessadorget
, mas nenhumset
) ou somente gravação (elas têm um acessadorset
, mas nenhumget
). As propriedades somente gravação são raras.
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#.