Devo preferir propriedades com ou sem campos particulares?

16

A base de código em que estou trabalhando agora tem a convenção de usar campos privados e propriedades públicas. Por exemplo, a maioria das classes tem seus membros definidos assim:

// Fields
private double _foo;
private double _bar;
private double _baz;

// Properties
public double Foo
{
    get{ return _foo; }
    set{ _foo = value; }
}

public double Bar
{
    get{ return _bar; }
    set{ _bar = value; }
}

public double Baz
{
    get{ return _baz; }
}

Eu sei que estes podem ser reescritos sem suas propriedades privadas internas:

public double Foo{ get; set; }
public double Bar{ get; set; }
public double Baz{ get; private set; }

Eu gostaria de alguma entrada sobre isso:

  • Existe uma boa razão para preferir o estilo mais antigo e explícito do que o mais novo e conciso?
  • Devo escrever novas classes usando o estilo conciso ou devo tentar corresponder o código mais antigo para obter consistência? A consistência vale o suficiente neste caso para justificar o formato mais antigo?
KChaloux
fonte
Veja esta pergunta e resposta .
Peter K.
@PeterK. Informativo. Não responde se devo ou não me preocupar com o estilo do resto do programa, ou se é um detalhe pequeno o suficiente para não importar.
KChaloux
@KChaloux: Entendido! É por isso que é um comentário, não uma resposta. :-)
Peter K.
@PeterK. Fair 'nuff = p
KChaloux
1
outra razão para não a mudança é se alguma coisa é passado sobre o fio com WCF - propriedades automáticas têm questões de contrato (devido aos caracteres inválidos nos nomes de campos de apoio criados pelo .NET)
Leon

Respostas:

16

Existem algumas instâncias em que o chamado estilo "mais antigo" ainda é necessário:

R: Tipos imutáveis ​​usando a imutabilidade fornecida pelo idioma. O readonlymodificador em C # congela esse valor após a construção. Ainda não há como imitar isso com propriedades implementadas automaticamente.

public sealed class FooBarBazInator
{
    private readonly double foo;

    public FooBarBazInator(double foo)
    {
        this.foo = foo;
    }

    public double Foo
    {
        get
        {
            return this.foo;
        }
    }
}

B: Seus getters / setters têm qualquer lógica. Os códigos WPF e Silverlight (e similares) vinculados a suas classes serão implementados da seguinte INotifyPropertyChangedmaneira:

public class FooBarBazInator : INotifyPropertyChanged
{
    private double foo;

    public event PropertyChangedEventHandler PropertyChanged;

    public double Foo
    {
        get
        {
            return this.foo;
        }

        set
        {
            this.foo = value;
            this.RaisePropertyChanged("Foo");
        }
    }

    private void RaisePropertyChanged(string propertyName)
    {
        var propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
        {
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Além desses, eu diria que use o novo estilo. Mantém seu código conciso e oferece menos área de superfície para os bugs surgirem. Além disso, se precisar mudar para incluir a lógica, não será incompatível com a assinatura.

Jesse C. Slicer
fonte
2
Todo mundo parece concordar em seguir com o novo estilo. Você recebe um +1 e o crédito de resposta por me falar sobre a necessidade de um campo com o readonlymodificador, o que eu não sabia. = D
KChaloux
3
Alguns (inclusive eu) consideram as propriedades automáticas com acessador privado "boas o suficiente" para propriedades imutáveis. É verdade que eles podem não ser realmente imutáveis, mas você só precisa ter cuidado para não usar o setter fora do construtor.
svick
2
outra reaon é se você quer passar o valor como refo que não funciona com propriedades, então você precisa do campo
Stijn
1
Uma ferramenta como o Fody pode ser implementada INotifyPropertyChangedsem a necessidade de campos de apoio explícitos. github.com/Fody/PropertyChanged #
Sebastian Redl
3
Tome nota: o C # 6 permite "propriedades automáticas somente para getter", que fornecem em uma única linha o que meu exemplo "A" está fazendo.
Jesse C. Slicer
6

Eu diria que ter um campo de apoio explícito é apenas uma crosta inútil: torna seu código mais longo e difícil de ler. Ele também adiciona potencial de erro:

public double Bar
{
    get { return _baz; }
    set { _bar = value; }
}

Acho que um erro como esse poderia acontecer com bastante facilidade e seria bastante difícil de detectar.

E se você deseja consistência, faça o contrário: altere o código que usa campos de backup em código que usa propriedades automáticas. Isso é especialmente fácil se você usar uma ferramenta de refatoração como o ReSharper (e se você não usar algo assim, acho que deveria começar).

Obviamente, ainda existem casos em que ter campo de apoio é necessário, mas acho que isso não é relevante para esta questão.

svick
fonte
Na verdade, eu fiz isso em alguns dos campos antigos ao refatorar o código ... é uma dor.
KChaloux
1

Embora a pergunta seja bastante antiga, pensei em acrescentar alguns pontos que li em um livro que vale a pena mencionar aqui.

  1. Os mecanismos de serialização em tempo de execução mantêm o nome do arquivado em um fluxo serializado. O nome do campo de apoio para uma Propriedade implementada automaticamente (AIP) é determinado pelo compilador e pode realmente alterar o nome desse campo de apoio toda vez que você recompilar seu código, negando a capacidade de desserializar instâncias de qualquer tipo que contenha um AIP. Portanto, não use AIP com nenhum tipo que você queira serializar ou desserializar.

  2. Ao depurar, você não pode colocar um ponto de interrupção em um método AIP get ou set, portanto, não é possível detectar facilmente quando um aplicativo está obtendo ou configurando essa propriedade. Você pode definir pontos de interrupção em pontos de interrupção implementados manualmente

RAM
fonte
3
# 1 - A maioria dos serializadores ignora propriedades / campos privados por padrão; Nunca encontrei os problemas mencionados. # 2 - Isso foi corrigido no VS2015 e pode ser contornado no VS2013. Veja este artigo da Visual Studio Magazine .
Brian
-3

Existe uma boa razão para preferir o estilo mais antigo e explícito do que o mais novo e conciso?

Na verdade não. Embora eu argumentasse que sempre que você passou por uma propriedade como essa, deveria ter usado um campo público para começar.

Devo escrever novas classes usando o estilo conciso ou devo tentar corresponder o código mais antigo para obter consistência? A consistência vale o suficiente neste caso para justificar o formato mais antigo?

Supondo que você ignorou o conselho acima e tenha propriedades de passagem, não pretendo corresponder ao estilo. Idealmente, você retornaria e refatoraria o estilo antigo para o novo para ser consistente, mas a chance de as pessoas interpretarem mal esse código é pequena.

Telastyn
fonte
@svick - Bah. A menos que você esteja refletindo sobre o tipo, não há diferença. Se você estiver publicamente expondo o tipo como uma interface de serviço ou biblioteca, exporá uma interface que exigirá uma propriedade de qualquer maneira. O OP não está fazendo nada disso.
Telastyn
2
Não há razão técnica nesses casos limitados. Não esqueça que as propriedades se comportam de maneira diferente no depurador, diagramas de classes, intellisense e, quando você pensa que não está refletindo, a estrutura do .NET é. O WinForms, o WPF e vários outros componentes usam internamente a reflexão e tratam as propriedades separadamente. Portanto, os conselhos para usar campos públicos, sempre, serão mal recebidos por muitos desenvolvedores neste site.
Kevin McCormick
1
O @KevinMcCormick e o framework se comportam perfeitamente bem (pelo que entendi) com os campos. Realmente não importa, já que existem propriedades automáticas agora, mas o maior problema é tornar as coisas públicas em primeiro lugar. De longe, muitas pessoas pensam que simplesmente tornar as coisas propriedades de alguma forma as absolve de 'não crie campos públicos'.
Telastyn
2
O Framework lida com eles de maneira diferente: tente usar um campo público no EF POCO, vincule-o a um DataGridView, use um PropertyGrid, etc. Acabei de fazer uma pesquisa rápida no meu descompilador para Type.GetProperties / GetProperty e o Framework faz chamadas para esse método centenas de vezes, mas não para o GetField. Resumindo, eles são diferentes, e a recomendação de sempre usar propriedades para dados públicos se baseia em parte nesse fato. Isso nunca impede o uso de campos, mas esse caso precisa de justificativa. No entanto, seu outro argumento é definitivamente verdadeiro, expor dados públicos descuidadamente é sempre uma má ideia.
21412 Kevin McCormick