Partilhar via


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 _nameprivado :

    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, , , internalprotected, protected internal, ou private protectedprivate. 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 propriedade Name na classe base. Nesse caso, o new 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 Squareuma classe Shapeabstrata 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
*/

Consulte também