Usando propriedades (Guia de Programação em C#)
As propriedades combinam aspectos de métodos e campos. Para o usuário de um objeto, uma propriedade parece como um campo; o acesso à propriedade exige a mesma sintaxe. Para o implementador de uma classe, uma propriedade é um ou dois blocos de código, representando um acessador get
e/ou um ou acessador set
OU init
. O bloco de código do acessador get
é executado quando a propriedade é lida; o bloco de código do acessador set
ou init
é executado quando a propriedade recebe um valor. Uma propriedade sem um acessador set
é considerada como somente leitura. Uma propriedade sem um acessador get
é considerada como somente gravação. Uma propriedade que tem os dois acessadores é leitura/gravação. Você pode usar um acessador init
em vez de um acessador set
para permitir que a propriedade seja definida como parte da inicialização do objeto, mas de outra forma torná-la somente leitura.
Diferentemente dos campos, as propriedades não são classificadas como variáveis. Portanto, você não pode passar uma propriedade como um parâmetro ref
ou out
.
As propriedades têm muitos usos:
- Elas podem validar dados antes de permitir uma alteração.
- Elas podem expor dados de forma transparente em uma classe em que esses dados são recuperados de alguma outra fonte, como um banco de dados.
- Elas podem executar uma ação quando os dados são alterados, como gerar um evento ou alterar o valor de outros campos.
As propriedades são declaradas no bloco de classe, especificando o nível de acesso do campo, seguido pelo tipo da propriedade, pelo nome da propriedade e por um bloco de código que declara um acessador get
e/ou um acessador set
. Por exemplo:
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
Neste exemplo, Month
é declarado como uma propriedade de maneira que o acessador set
possa garantir que o valor Month
esteja definido entre 1 e 12. A propriedade Month
usa um campo particular para rastrear o valor real. O local real dos dados de uma propriedade é frequentemente chamado de "repositório de backup" da propriedade. É comum que as propriedades usem campos privados como repositório de backup. O campo é marcado como particular para garantir que ele só pode ser alterado ao chamar a propriedade. Para obter mais informações sobre as restrições de acesso público e particular, consulte Modificadores de acesso. As propriedades implementadas automaticamente fornecem sintaxe simplificada para declarações de propriedade simples. Para obter mais informações, consulte Propriedades implementadas automaticamente.
A partir do C# 13, você pode usar propriedades com suporte de campo para adicionar validação ao set
acessador de uma propriedade implementada automaticamente, conforme mostrado no exemplo a seguir:
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
Importante
A field
palavra-chave é um recurso de visualização no C# 13. Você deve estar usando o .NET 9 e definir seu <LangVersion>
elemento como preview
no arquivo de projeto para usar a field
palavra-chave contextual.
Você deve ter cuidado ao usar o recurso de field
palavra-chave em uma classe que tenha um campo chamado field
. A nova field
palavra-chave sombreia um campo nomeado field
no escopo de um acessador de propriedade. Você pode alterar o nome da field
variável ou usar o @
token para fazer referência ao field
identificador como @field
. Você pode saber mais lendo a especificação do recurso para a field
palavra-chave.
O acessador get
O corpo do acessador get
assemelha-se ao de um método. Ele deve retornar um valor do tipo de propriedade. O compilador C# e o compilador just-in-time (JIT) detectam padrões comuns para implementar o acessador get
, e otimiza esses padrões. Por exemplo, um acessador get
que retorna um campo sem executar qualquer computação provavelmente é otimizado para uma leitura de memória desse campo. As propriedades implementadas automaticamente seguem esse padrão e se beneficiam dessas otimizações. No entanto, um método acessador virtual get
não pode ser embutido porque o compilador não sabe em tempo de compilação qual método pode realmente ser chamado em tempo de execução. O seguinte exemplo mostra um acessador get
que retorna o valor de um campo particular _name
:
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
Quando você referencia a propriedade, exceto como o destino de uma atribuição, o acessador get
é invocado para ler o valor da propriedade. Por exemplo:
var employee= new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
O acessador get
deve ser um membro com corpo de expressão ou terminar em uma instrução return ou throw, e o controle não pode fluir para fora do corpo do acessador.
Aviso
Geralmente, é um estilo de programação ruim alterar o estado do objeto usando o get
acessador. Uma exceção a essa regra é uma propriedade avaliada lentamente, em que o valor de uma propriedade é calculado somente quando ela é acessada pela primeira vez.
O acessador get
pode ser usado para retornar o valor do campo ou para calculá-lo e retorná-lo. Por exemplo:
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
No exemplo anterior, se você não atribuir um valor à propriedade Name
, ela retornará o valor NA
.
O acessador set
O acessador set
é semelhante a um método cujo tipo de retorno é void. Ele usa uma parâmetro implícito chamado value
, cujo tipo é o tipo da propriedade. O compilador e o compilador JIT também reconhecem padrões comuns para um acessador set
ou init
. Esses padrões comuns são otimizados, gravando diretamente a memória para o campo de backup. No exemplo a seguir, uma acessador set
é adicionado à propriedade Name
:
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
Quando você atribui um valor à propriedade, o acessador set
é invocado por meio do uso de um argumento que fornece o novo valor. Por exemplo:
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
É um erro usar o nome de parâmetro implícito value
, para uma declaração de variável local em um acessador set
.
O acessador init
O código para criar um acessador init
é o mesmo que o código para criar um acessador set
, exceto que você usa a palavra-chave init
em vez de set
. A diferença é que o acessador init
só pode ser empregado no construtor ou usando um inicializador de objeto.
Comentários
As propriedades podem ser marcadas como public
, private
, protected
, internal
, protected internal
ou private protected
. Esses modificadores de acesso definem como os usuários da classe podem acessar a propriedade. Os acessadores get
e set
da mesma propriedade podem ter modificadores de acesso diferentes. Por exemplo, get
pode ser public
para permitir o acesso somente leitura de fora do tipo, e set
pode ser private
ou protected
. Para obter mais informações, consulte Modificadores de Acesso.
Uma propriedade pode ser declarada como uma propriedade estática usando a palavra-chave static
. As propriedades estáticas estão disponíveis para chamadores a qualquer momento, mesmo se não existir nenhuma instância da classe. Para obter mais informações, consulte Classes estáticas e membros de classes estáticas.
Uma propriedade pode ser marcada como uma propriedade virtual usando a palavra-chave virtual. As propriedades virtuais permitem que classes derivadas substituam o comportamento da propriedade, usando a palavra-chave override. Para obter mais informações sobre essas opções, consulte Herança.
Uma propriedade que substitui uma propriedade virtual também pode ser sealed, especificando que ela não é mais virtual para classes derivadas. Por fim, uma propriedade pode ser declarada abstract. As propriedades abstratas não definem nenhuma implementação na classe e as classes derivadas devem escrever sua própria implementação. Para obter mais informações sobre essas opções, consulte Classes e membros de classes abstract e sealed.
Observação
É um erro usar um modificador virtual, abstract ou override em um acessador de uma propriedade static.
Exemplos
Este exemplo demonstra as propriedades instância, estática e somente leitura. Ele aceita o nome do funcionário digitado no teclado, incrementa NumberOfEmployees
em 1 e exibe o nome e o número do funcionário.
public class Employee
{
public static int NumberOfEmployees;
private static int _counter;
private string _name;
// A read-write instance property:
public string Name
{
get => _name;
set => _name = value;
}
// A read-only static property:
public static int Counter => _counter;
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}
Exemplo de propriedade oculta
Este exemplo demonstra como acessar uma propriedade em uma classe base que está oculta por outra propriedade que tem o mesmo nome em uma classe derivada:
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Manager : Employee
{
private string _name;
// Notice the use of the new modifier:
public new string Name
{
get => _name;
set => _name = value + ", Manager";
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
// Derived class property.
m1.Name = "John";
// Base class property.
((Employee)m1).Name = "Mary";
System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
}
}
/* Output:
Name in the derived class is: John, Manager
Name in the base class is: Mary
*/
A seguir estão os pontos importantes do exemplo anterior:
- A propriedade
Name
na classe derivada oculta a propriedadeName
na classe base. Nesse caso, o modificadornew
é usado na declaração da propriedade na classe derivada:public new string Name
- A conversão
(Employee)
é usada para acessar a propriedade oculta na classe base:((Employee)m1).Name = "Mary";
Para obter mais informações sobre como ocultar membros, consulte o Modificador new.
Exemplo de substituição de propriedade
Neste exemplo, duas classes, Cube
e Square
, implementam uma classe abstrata Shape
e substituem sua propriedade Area
abstrata. Observe o uso do modificador override nas propriedades. O programa aceita o lado como uma entrada e calcula as áreas para o cubo e o quadrado. Ele também aceita a área como uma entrada e calcula o lado correspondente para o cubo e o quadrado.
abstract class Shape
{
public abstract double Area
{
get;
set;
}
}
class Square : Shape
{
public double side;
//constructor
public Square(double s) => side = s;
public override double Area
{
get => side * side;
set => side = System.Math.Sqrt(value);
}
}
class Cube : Shape
{
public double side;
//constructor
public Cube(double s) => side = s;
public override double Area
{
get => 6 * side * side;
set => side = System.Math.Sqrt(value / 6);
}
}
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s = new Square(side);
Cube c = new Cube(side);
// Display the results:
System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
System.Console.WriteLine();
// Input the area:
System.Console.Write("Enter the area: ");
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine("Side of the square = {0:F2}", s.side);
System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
}
}
/* Example Output:
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
*/