Há algum motivo para usar propriedades privadas em c #?

245

Acabei de perceber que a construção da propriedade C # também pode ser usada com um modificador de acesso privado :

private string Password { get; set; }

Embora isso seja tecnicamente interessante, não consigo imaginar quando o usaria, pois um campo privado envolve ainda menos cerimônia :

private string _password;

e não consigo imaginar quando precisaria obter internamente, mas não definir ou definir, mas não obter um campo privado:

private string Password { get; }

ou

private string Password { set; }

mas talvez exista um caso de uso com classes aninhadas / herdadas ou talvez um get / set possa conter lógica em vez de apenas devolver o valor da propriedade, embora eu tenderia a manter as propriedades estritamente simples e permitir que métodos explícitos fizessem qualquer lógica, por exemplo GetEncodedPassword().

Alguém usa propriedades privadas em C # por qualquer motivo ou é apenas uma daquelas construções tecnicamente possíveis, embora raramente usadas em código real?

Termo aditivo

Boas respostas, lendo-as, selecionei esses usos para propriedades particulares:

  • quando campos particulares precisam ser carregados lentamente
  • quando campos particulares precisam de lógica extra ou são valores calculados
  • pois os campos particulares podem ser difíceis de depurar
  • para "apresentar um contrato a si mesmo"
  • converter / simplificar internamente uma propriedade exposta como parte da serialização
  • agrupando variáveis ​​globais para serem usadas dentro da sua classe
Edward Tanguay
fonte
A técnica incentivada por propriedades particulares é auto encapsulamento - veja: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

Respostas:

212

Eu os uso se precisar armazenar em cache um valor e quiser carregá-lo com preguiça.

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}
Shaun Bowe
fonte
42
um padrão comum agradável para isto éreturn _password ?? (_password = CallExpensiveOperation());
Marc
1
O @EvAlex Lazy <T> pode ser preferível se você puder usá-lo. Mas ele só pode usar coisas estáticas, portanto você não pode acessar métodos (não estáticos) ou outros membros. A propósito, prefiro a sintaxe de linha única return _password ?? (_password = CallExpensiveOperation());há algum tempo. Ou, na verdade, o Resharper prefere :).
Bart
4
@ Marc desde C # 6, você nem precisa escrever um get e return. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher
1
Com o C # 8, é ainda mais curto:private string Password => _password ??= CallExpensiveOperation();
Dave M
2
@ Bas Esse não é um carregamento lento porque CallExpensiveOperation();é chamado durante a construção / inicialização do objeto que contém e não quando a propriedade é acessada pela primeira vez.
Stefan Podskubka 27/04
142

O principal uso disso no meu código é a inicialização lenta, como outros já mencionaram.

Outro motivo para propriedades privadas sobre campos é que as propriedades privadas são muito, muito mais fáceis de depurar do que os campos privados. Frequentemente, quero saber coisas como "esse campo está sendo configurado inesperadamente; quem é o primeiro chamador que define esse campo?" e é muito mais fácil se você simplesmente colocar um ponto de interrupção no levantador e pressionar ir. Você pode colocar o login lá. Você pode colocar métricas de desempenho lá. Você pode colocar verificações de consistência que são executadas na compilação de depuração.

Basicamente, tudo se resume a: código é muito mais poderoso que dados . Qualquer técnica que me permita escrever o código necessário é boa. Os campos não permitem que você escreva código neles, as propriedades fazem.

Eric Lippert
fonte
5
"O código é muito mais poderoso que os dados" está dizendo? No Google, ele retorna referências que apontam para você. Só quero saber para que eu possa citá-lo corretamente quando preciso.
Joan Venge 24/03
24
@Joan: eu não sei. Ou inventei ou ouvi alguém dizer e pensei "uau, eu deveria roubar isso totalmente e depois esquecer tudo de quem o roubei".
precisa
1
+ CodeLens informa onde a propriedade é referenciada
UuDdLrLrSs 20/02/19
43

talvez exista um caso de uso com classes aninhadas / herdadas ou talvez um get / set possa conter lógica em vez de apenas devolver o valor da propriedade

Eu pessoalmente uso isso mesmo quando não preciso de lógica no getter ou setter de uma propriedade. O uso de uma propriedade, mesmo privada, ajuda a proteger seu código para o futuro, para que você possa adicionar a lógica a um getter posteriormente, se necessário.

Se eu achar que uma propriedade pode eventualmente exigir lógica extra, algumas vezes a agruparei em uma propriedade privada em vez de usar um campo, apenas para não precisar alterar meu código posteriormente.


Em um caso semi-relacionado (embora diferente da sua pergunta), eu frequentemente uso os setters privados em propriedades públicas:

public string Password 
{
    get; 
    private set;
}

Isso fornece a você um getter público, mas mantém o setter privado.

Reed Copsey
fonte
O +1 faz sentido: "Se eu sentir que uma propriedade pode eventualmente exigir lógica extra, algumas vezes a agruparei em uma propriedade privada em vez de usar um campo, apenas para não precisar alterar meu código posteriormente".
Edward Tanguay
7
setters privados <3
Earlz
20

Um bom uso para obter propriedades privadas apenas são os valores calculados. Várias vezes tive propriedades que são privadas somente de leitura e apenas fazem um cálculo sobre outros campos no meu tipo. Não é digno de um método e não é interessante para outras classes, portanto é propriedade privada.

JaredPar
fonte
20

A inicialização lenta é um local onde elas podem ser organizadas, por exemplo

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Depois, você pode escrever: em this.MyTypetodo lugar, em vez de, this.mytype.Valuee encapsular o fato de que ele é instanciado preguiçosamente em um único local.

Uma coisa que é uma vergonha é que o C # não suporta o escopo do campo de suporte da propriedade (ou seja, declarando-o dentro da definição da propriedade) para ocultá-lo completamente e garantir que ele possa ser acessado apenas através da propriedade.

Greg Beech
fonte
2
Concordou que seria ter o aspecto de escopo lá.
Chris Marisic
5
Uso essa mesma técnica frequentemente e também desejei que um campo pudesse ter um escopo definido para um corpo de código. É um recurso interessante, mas de baixa prioridade.
Eric Lippert
5
@Eric Lippert - field-declarationo escopo do escopo accessor-declarationsestá classificado como número 1 na minha lista de desejos em C # há muito tempo. Se você conseguir que isso seja projetado e implementado em alguma versão futura (real), vou fazer um bolo para você.
Jeffrey L Whitledge
13

O único uso que eu consigo pensar

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}
Lukasz Madon
fonte
+1 para a classe útil de propriedades que são computados a partir de outras variáveis privadas
Daren Thomas
2
Por que não usar um método privadoprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman
10

Propriedades e campos não são um para um. Uma propriedade é sobre a interface de uma classe (seja sobre sua interface pública ou interna), enquanto um campo é sobre a implementação da classe. As propriedades não devem ser vistas como uma maneira de expor apenas os campos; elas devem ser vistas como uma maneira de expor a intenção e o objetivo da classe.

Assim como você usa propriedades para apresentar um contrato a seus consumidores sobre o que constitui sua classe, você também pode apresentar um contrato a si mesmo por razões muito semelhantes. Então, sim, eu uso propriedades privadas quando faz sentido. Às vezes, uma propriedade privada pode ocultar detalhes de implementação, como carregamento lento, o fato de que uma propriedade é realmente um conglomerado de vários campos e aspectos, ou que uma propriedade precisa ser virtualmente instanciada a cada chamada (pense DateTime.Now). Definitivamente, há momentos em que faz sentido impor isso mesmo a você mesmo no back-end da classe.

Matt Greer
fonte
+1: "você também pode apresentar um contrato para si mesmo por razões muito semelhantes" faz sentido
Edward Tanguay
8

Eu os uso em serialização, com coisas como DataContractSerializerou protobuf-net que suportam esse uso ( XmlSerializernão). É útil se você precisar simplificar um objeto como parte da serialização:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}
Marc Gravell
fonte
6

Uma coisa que faço o tempo todo é armazenar variáveis ​​"globais" / cache no HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}
Earlz
fonte
5

Eu os uso de vez em quando. Eles podem facilitar a depuração de itens quando você pode colocar facilmente um ponto de interrupção na propriedade ou adicionar uma instrução de log etc.

Também pode ser útil se você precisar alterar posteriormente o tipo de dados de alguma forma ou se precisar usar reflexão.

Hans Olsson
fonte
Idem; se houver lógica envolvida em um get / set, às vezes posso usar uma propriedade privada ou protegida. Geralmente depende de quanta lógica: lógica simples que farei na propriedade, muita lógica usarei normalmente uma função auxiliar. O que quer que torne o código mais sustentável.
TechNeilogy
5

Uso propriedades privadas para reduzir o código para acessar subpropriedades que costumam ser usadas.

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

É útil se houver muitas subpropriedades.

Yohanes Nurcahyo
fonte
2

É uma prática comum modificar apenas membros com métodos get / set, mesmo privados. Agora, a lógica por trás disso é que você saiba que seu get / set sempre se comporta de uma maneira específica (por exemplo, disparando eventos) que não parece fazer sentido, pois esses não serão incluídos no esquema de propriedades ... mas velhos hábitos morrem com força.

corsiKa
fonte
2

Faz todo o sentido quando existe uma lógica associada ao conjunto de propriedades ou get (pense na inicialização lenta) e a propriedade é usada em alguns lugares da classe.

Se é apenas um campo de apoio direto? Nada vem à mente como uma boa razão.

Marc
fonte
2

Eu sei que esta pergunta é muito antiga, mas as informações abaixo não estavam em nenhuma das respostas atuais.

Não consigo imaginar quando precisaria obter internamente, mas não definir

Se você estiver injetando suas dependências, poderá ter um Getter em uma propriedade e não um setter, pois isso indicaria uma propriedade somente leitura. Em outras palavras, a propriedade pode ser definida apenas no construtor e não pode ser alterada por nenhum outro código da classe.

O Visual Studio Professional também fornecerá informações sobre uma propriedade e não sobre um campo, facilitando a visualização do que seu campo está sendo usado.

PorpField

Cara
fonte
1

Bem, como ninguém mencionou, você pode usá-lo para validar dados ou bloquear variáveis.

  • Validação

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Travamento

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Nota: Ao bloquear uma referência, você não bloqueia o acesso aos membros do objeto referenciado.

Referência preguiçosa: Quando o carregamento é lento, você pode precisar assiná- lo de forma assíncrona, para a qual atualmente existe o AsyncLazy . Se você estiver em versões anteriores ao Visual Studio SDK 2015 ou não o estiver usando, também poderá usar o AsyncLazy do AsyncEx .

Lucas Martins Juviniano
fonte
0

Alguns usos mais exóticos de campos explícitos incluem:

  • você precisa usar refou outcom o valor - talvez porque seja umInterlocked contador
  • ele pretende representar o layout fundamental, por exemplo, em um structlayout explícito (talvez para mapear para um despejo de C ++, ouunsafe código em )
  • historicamente, o tipo foi usado com BinaryFormattero tratamento automático de campos (alterar para adereços automáticos altera os nomes e, portanto, interrompe o serializador)
Marc Gravell
fonte