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 ser um campo. Acessar a propriedade requer a mesma sintaxe. Para o implementador de uma classe, uma propriedade consiste em um ou dois blocos de código, que representam um acessador get e/ou um acessador set. O bloco de código para o acessador get é executado quando a propriedade é lida. O bloco de código para o acessador set é executado quando um novo valor é atribuído à propriedade. 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. No C# 9 e posterior, você pode usar um init acessador em vez de um set acessador para tornar a propriedade 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 os dados antes de permitir uma alteração; elas podem expor, de forma transparente, os dados em uma classe em que esses dados são realmente recuperados de outra origem qualquer, como um banco de dados; elas podem executar uma ação quando os dados são alterados, como acionar 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 suporte. 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 autoimplementadas fornecem sintaxe simplificada para declarações de propriedade simples. Para obter mais informações, consulte Propriedades autoimplementadas.

O acessador get

O corpo do acessador get assemelha-se ao de um método. Ele deve retornar um valor do tipo de propriedade. A execução do acessador get é equivalente à leitura do valor do campo. Por exemplo, quando você estiver retornando a variável particular do acessador get e as otimizações estiverem habilitadas, a chamada ao método do acessador get será embutida pelo compilador, portanto, não haverá nenhuma sobrecarga de chamada de método. No entanto, um método do acessador get virtual não pode ser embutido porque o compilador não sabe, em tempo de compilação, qual método pode ser chamado em tempo de execução. A seguir está um acessador get que retorna o valor de um campo particular _name:

class Person
{
    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:

Person person = new Person();
//...

System.Console.Write(person.Name);  // the get accessor is invoked here

O acessador get deve terminar em uma instrução return ou throw e o controle não pode fluir para fora do corpo do acessador.

Alterar o estado do objeto usando o acessador get é um estilo ruim de programação. Por exemplo, o acessador a seguir produz o efeito colateral de alterar o estado do objeto sempre que o campo _number é acessado.

private int _number;
public int Number => _number++;	// Don't do this

O acessador get pode ser usado para retornar o valor do campo ou para calculá-lo e retorná-lo. Por exemplo:

class Employee
{
    private string _name;
    public string Name => _name != null ? _name : "NA";
}

No segmento de código anterior, se você não atribuir um valor à Name propriedade, ele retornará o valor NA.

O acessador de conjunto

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. No exemplo a seguir, uma acessador set é adicionado à propriedade Name:

class Person
{
    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:

Person person = new Person();
person.Name = "Joe";  // the set accessor is invoked here

System.Console.Write(person.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 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.

Comentários

As propriedades podem ser marcadas como public, private, , protected, internal, protected internalou private protected. Esses modificadores de acesso definem como os usuários da classe podem acessar a propriedade. Os acessadores get e set para a mesma propriedade podem ter modificadores de acesso diferentes. Por exemplo, o get pode ser public para permitir acesso 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 palavra-chave static. Isso torna a propriedade disponível 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 classe estática.

Uma propriedade pode ser marcada como uma propriedade virtual, usando a palavra-chave virtual. Isso habilita as classes derivadas a substituírem 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. Isso significa que não há 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:
}

class TestEmployee
{
    static void Main()
    {
        Employee.NumberOfEmployees = 107;
        Employee e1 = new Employee();
        e1.Name = "Claude Vige";

        System.Console.WriteLine("Employee number: {0}", Employee.Counter);
        System.Console.WriteLine("Employee name: {0}", e1.Name);
    }
}
/* Output:
    Employee number: 108
    Employee name: Claude Vige
*/

Exemplo de propriedade oculta

Este exemplo demonstra como acessar uma propriedade em uma classe base 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
{
    static void Main()
    {
        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 propriedade Name na classe base. Nesse caso, o modificador new é 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 da propriedade Override

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
*/

Confira também