Usando propriedades (Guia de Programação em C#)
As propriedades combinam aspetos de ambos os campos e métodos. Para o usuário de um objeto, uma propriedade parece ser um campo; O acesso à propriedade requer a mesma sintaxe. Para o implementador de uma classe, uma propriedade é um ou dois blocos de código, representando um get
acessador e/ou um set
ou init
acessador. O bloco de código para o get
acessador é executado quando a propriedade é lida, o bloco de código para o set
ou init
acessador é executado quando a propriedade recebe um valor. Uma propriedade sem um set
acessador é considerada somente leitura. Uma propriedade sem um get
acessador é considerada somente gravação. Uma propriedade que tem ambos os acessadores é leitura-gravação. Você pode usar um init
acessador em vez de um set
acessador para permitir que a propriedade seja definida como parte da inicialização do objeto, mas, caso contrário, torná-la somente leitura.
Ao contrário dos campos, as propriedades não são classificadas como variáveis. Portanto, você não pode passar uma propriedade como um ref
ou out
parâmetro.
As propriedades têm muitas utilizações:
- Eles podem validar dados antes de permitir uma alteração.
- Eles podem expor dados de forma transparente em uma classe onde esses dados são recuperados de alguma outra fonte, como um banco de dados.
- Eles 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, seguido pelo nome da propriedade e seguido por um bloco de código que declara um get
-accessor e/ou um set
accessor. 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 para que o set
acessador possa certificar-se de que o Month
valor está definido entre 1 e 12. A Month
propriedade usa um campo privado para acompanhar o valor real. A localização real dos dados de uma propriedade é muitas vezes referida como a "loja de apoio" da propriedade. É comum que os estabelecimentos usem campos privados como armazenamento de suporte. O campo é marcado como privado para garantir que só pode ser alterado ligando para a propriedade. Para obter mais informações sobre restrições de acesso público e privado, 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 backup 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 em C# 13. Você deve estar usando o .NET 9 e definir seu <LangVersion>
elemento como preview
em seu 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 tem 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 acessor get
O corpo do acessador se assemelha ao get
de um método. Ele deve retornar um valor do tipo de propriedade. O compilador C# e o compilador Just-in-time (JIT) detetam padrões comuns para implementar o get
acessador e otimizam esses padrões. Por exemplo, um get
acessador que retorna um campo sem executar qualquer cálculo 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 de 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 exemplo a seguir mostra um get
acessador que retorna o valor de um campo _name
privado :
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
Quando você faz referência à propriedade, exceto como o destino de uma atribuição, o get
acessador é 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 get
acessador 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 do corpo do accessor.
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 preguiçosa, onde o valor de uma propriedade é calculado apenas quando ela é acessada pela primeira vez.
O get
acessador pode ser usado para retornar o valor do campo ou para calculá-lo e devolvê-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 à Name
propriedade, ela retornará o valor NA
.
O acessador definido
O set
acessador se assemelha a um método cujo tipo de retorno é nulo. Ele usa um 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 set
ou init
acessador. Esses padrões comuns são otimizados, gravando diretamente a memória para o campo de apoio. No exemplo a seguir, um set
acessador é adicionado à Name
propriedade:
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 é invocado set
usando 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 do parâmetro implícito, value
, para uma declaração de variável local em um set
acessador.
O acessador init
O código para criar um init
acessador é o mesmo que o código para criar um set
acessador, exceto que você usa a init
palavra-chave em vez de set
. A diferença é que o init
acessador só pode ser usado no construtor ou usando um inicializador de objeto.
Observações
As propriedades podem ser marcadas como public
, , , internal
protected
, protected internal
, ou private protected
private
. Esses modificadores de acesso definem como os usuários da classe podem acessar a propriedade. Os get
e set
acessadores para a mesma propriedade podem ter modificadores de acesso diferentes. Por exemplo, o pode ser public
permitir acesso get
somente leitura de fora do tipo, e o 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 static
palavra-chave. As propriedades estáticas estão disponíveis para chamadores a qualquer momento, mesmo que nenhuma instância da classe exista. 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 as 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 selada, especificando que, para classes derivadas, ela não é mais virtual. Por fim, uma propriedade pode ser declarada abstrata. As propriedades abstratas não definem uma 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 abstratas e seladas e Membros de classe.
Nota
É um erro usar um modificador virtual, abstrato ou de substituição em um acessador de uma propriedade estática .
Exemplos
Este exemplo demonstra propriedades de instância, estáticas e somente leitura. Ele aceita o nome do funcionário do teclado, incrementa em NumberOfEmployees
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 pontos importantes no exemplo anterior:
- A propriedade
Name
na classe derivada oculta a propriedadeName
na classe base. Nesse caso, onew
modificador é usado na declaração da propriedade na classe derivada:public new string Name
- O cast
(Employee)
é usado para acessar a propriedade oculta na classe base:((Employee)m1).Name = "Mary";
Para obter mais informações sobre como ocultar membros, consulte o novo Modificador.
Exemplo de propriedade de substituição
Neste exemplo, Cube
duas classes e , implementam Square
uma classe Shape
abstrata e substituem sua propriedade abstrata Area
. Observe o uso do modificador de substituição nas propriedades. O programa aceita o lado como entrada e calcula as áreas para o quadrado e cubo. Ele também aceita a área como uma entrada e calcula o lado correspondente para o quadrado e cubo.
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
*/