Propriedades do .NET - Usar conjunto privado ou propriedade ReadOnly?


45

Em que situação devo usar um conjunto privado em uma propriedade em vez de torná-lo uma propriedade ReadOnly? Leve em consideração os dois exemplos muito simplistas abaixo.

Primeiro exemplo:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

Segundo exemplo:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

Ambos produzem os mesmos resultados. É uma situação em que não há certo e errado, e é apenas uma questão de preferência?


public string Name { get; protected set; }através da herança.
— Samis 02/04

Respostas:


42

Existem algumas razões para usar private set.

1) Se você não está usando um campo de suporte e deseja uma propriedade automática somente leitura:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) Se você deseja fazer um trabalho extra ao modificar a variável dentro da sua classe e deseja capturá-la em um único local:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

Em geral, porém, é uma questão de preferência pessoal. Até onde eu sei, não há razões de desempenho para usar um sobre o outro.


1
Apenas adicionando isso porque a pergunta também possui uma tag vb.net, mas no vb.net você precisa especificar um suporte se usar privado em get ou set. Então, no vb.net, na verdade, é menos trabalhoso tornar a propriedade somente de leitura, eu acho.
— user643192

Eu nunca soube disso private set. :-)
— Afzaal Ahmad Zeeshan

10
Uma atualização para aqueles que estão lendo esta resposta em 2016. C # 6.0 introduziu auto-propriedades somente leitura, que permitem que você tenha uma propriedade somente leitura sem um campo de apoio: public string Name { get; }. Se você não deseja uma propriedade mutável, essa é a sintaxe preferida agora.
— 22416 Alexey

4
Uma boa razão para não usar private seté que não é tão imutável quanto gostamos de fingir. Se você deseja implementar uma classe verdadeiramente imutável, somente leitura é uma obrigação.
— RubberDuck

Pode ser um motivo de desempenho para NÃO usar somente leitura. Parece causar cópia desnecessária de estruturas ao acessar métodos de um campo de estrutura somente leitura. codeblog.jonskeet.uk/2014/07/16/…
— Triynko

28

Use conjunto privado quando quiser que o setter não possa ser acessado de fora .

Use somente leitura quando desejar definir a propriedade apenas uma vez . No construtor ou inicializador de variável.

TESTE ESTA:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}

Embora eu concorde com a sua resposta, seu exemplo pode melhorar. Você pode fazer um comentário onde o erro do compilador ocorrerá.
— Michael Richardson

NB A sintaxe do VB.NET correspondente à readonlypalavra-chave C # é aplicada ReadOnlyao campo, e não à propriedade.
— Zev Spitz 03/01

8

Posso sugerir uma terceira opção?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

Isso torna a propriedade Name efetivamente somente leitura para todo o código externo e fornece um método Set explícito. Prefiro o conjunto explícito do que simplesmente usar o conjunto na propriedade Name porque você está alterando o valor ao defini-lo. Normalmente, se você definir um valor de propriedade, espera recuperar o mesmo valor ao chamar o get mais tarde, o que não aconteceria se você definisse o ToTitleCase no conjunto .

No entanto, como você disse, não há uma resposta certa.


Acredito que 'conjunto privado' possui semântica especial no compilador (não apenas como um acessador privado). Este também é o caso do conjunto protegido? Caso contrário, onde está o equivalente semântico ao conjunto protegido se o conjunto privado tiver semântica especial? Não consegui encontrar nenhuma documentação explicando isso.
— Sprague

1
+1, mas eu chamaria o método "Renomear" em vez de "SetName".
— MattDavey


4

Não use o segundo exemplo. O objetivo principal de usar uma propriedade - mesmo que não ocorra nada além do getter get e da configuração do setter - é canalizar todo o acesso através desse getter e setter, para que, se você precisar mudar o comportamento no futuro, tudo esteja em ordem. um lugar.

Seu segundo exemplo abandona isso no caso de definir a propriedade. Se você usasse essa abordagem em uma classe grande e complexa, e mais tarde precisasse mudar o comportamento da propriedade, estaria em busca e substituição de terrenos, em vez de fazer a alteração em um só lugar - o instalador particular.


2

Sempre que eu precisei alterar o nível de acesso de um setter, geralmente o alterei para Protected (somente esta classe e classes derivadas podem alterar o valor) ou Friend (somente membros da minha montagem podem alterar o valor).

Mas usar Privado faz todo sentido quando você deseja executar outras tarefas no setter, além de alterar o valor de apoio. Conforme mencionado anteriormente, é bom design não referenciar diretamente seus valores de apoio, mas acessá-los apenas por meio de suas propriedades. Isso garante que as alterações posteriores feitas em uma propriedade sejam aplicadas tanto internamente quanto externamente. E praticamente não há penalidade de desempenho em referenciar uma propriedade versus sua variável de suporte.


0

E praticamente não há penalidade de desempenho ...

Mas, para esclarecer, acessar uma propriedade é mais lento que acessar sua variável de backup. O getter e o setter de uma propriedade são métodos que requerem uma chamada e um retorno, enquanto a variável de backup de uma propriedade é acessada diretamente.

É por isso que, nos casos em que o getter de uma propriedade pode ser acessado várias vezes em um bloco de código, o valor da propriedade é algumas vezes armazenado em cache primeiro (salvo em uma variável local) e a variável local usada em seu lugar. Obviamente, isso pressupõe que a propriedade não pode ser alterada de forma assíncrona enquanto o bloco está em execução.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.