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?

tgxiii
fonte
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.

Adam Lear
fonte
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
    }
}
asakura89
fonte
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.

Dave Wise
fonte
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.

Carson63000
fonte
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.

Prlaba
fonte
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.

Prlaba
fonte