Variável privada vs propriedade?

41

Ao definir um valor para uma variável dentro de uma classe na maioria das vezes, são apresentadas duas opções:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Existe uma convenção que determina como devemos atribuir valores a variáveis ​​dentro de nossas classes? Por exemplo, se eu tiver um método dentro da mesma classe, devo atribuí-lo usando a propriedade ou a variável privada Eu já vi isso nos dois sentidos, então eu queria saber se isso é uma escolha ou desempenho é um fator (menor, provavelmente).

Edward
fonte

Respostas:

23

Eu daria um passo adiante e o levaria a três casos. Embora existam variações em cada uma, estas são as regras que uso na maioria das vezes na programação em C #.

Nos casos 2 e 3, sempre vá para o Acessador de propriedades (não a variável de campo). E no caso 1, você é salvo de ter que fazer essa escolha.

1.) Propriedade imutável (passada para o construtor ou criada no momento da construção). Nesse caso, eu uso uma variável de campo, com uma propriedade somente leitura. Eu escolho isso ao invés de um setter privado, pois um setter privado não garante imutabilidade.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) variável POCO. Uma variável simples que pode obter / definir em qualquer escopo público / privado. Nesse caso, eu usaria apenas uma propriedade automática.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Propriedades de ligação do ViewModel. Para classes que suportam INotifyPropertyChanged, acho que você precisa de uma variável de campo de apoio privada.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}
Sheldon Warkentin
fonte
2
+1 para o exemplo MVVM. Na verdade, foi isso que provocou a pergunta em primeiro lugar.
Edward
4
+1: Misture 2/3 com AOP e você terá uma ótima maneira de usar o INPC. [Notifica] public int Foo {get; conjunto; }
Steven Evers
1
@Job Para qualquer classe que acessa a classe, um setter privado é suficiente para imutabilidade. No entanto, dentro da classe, o configurador privado não impede configurações repetidas do valor, após a construção inicial. Um recurso de idioma como um 'conjunto privado somente de leitura' pode conceitualmente solucionar esse problema, mas ele não existe.
Sheldon Warkentin
1
Eu sou novo em C #, então me diga, por que usar em public int Foo {get; set;}vez de public int Foo?
1
Se uma classe ou estrutura se comportar como POCO ou PODS, que vantagem real há para agrupar campos nas propriedades? Entendo perfeitamente que a quebra de campos em propriedades é útil quando uma classe ou estrutura precisa, ou pode precisar no futuro, manter invariantes com relação ao seu conteúdo (talvez garantindo que outros objetos sejam atualizados para correspondê-los), mas se uma classe ou estrutura especifica que os consumidores podem escrever valores em qualquer ordem, sem restrições ou efeitos colaterais, que comportamentos úteis poderiam ser adicionados a um membro acessador?
Supercat 16/08
18

Geralmente, eu diria atribuir ao campo no construtor e usar a propriedade em qualquer outro lugar. Dessa forma, se alguém adicionar funcionalidade à propriedade, você não a perderá em lugar algum.

Certamente não é um fator de desempenho. O otimizador incluirá um get ou set simples para você e o código MSIL final provavelmente será idêntico.

pdr
fonte
Algum motivo específico para usar o campo no construtor? Menos chance de efeitos colaterais estranhos?
Sinal
4
@Sign: Meu palpite é que, se houver validação na propriedade (agora ou no futuro), você não deseja correr o risco de falhar na validação durante a construção. A validação não é lógica neste estágio, porque não é possível garantir que o objeto seja estável até que o construtor termine.
Steven Evers
@Sign: Tanto o que você disse quanto o que Snorfus disse. Ou, se eu quiser registrar alterações em uma propriedade, provavelmente não desejo registrar a configuração inicial. Mas eu disse "geralmente".
pdr
3
@Sign: o problema é: se o método definido da propriedade puder ser substituído em uma subclasse, você poderá ter um efeito colateral durante a criação do objeto ou de um objeto inconsistente (ou seja: a propriedade substituída está programada para não definir nenhum valor para esse campo). O uso de propriedades em construtores só é seguro se o método set for privado ou se a classe for selada.
Diego
4

Depende.

Primeiro, você deve preferir propriedades automáticas quando possível:

public string MyValue {get;set;}

Segundo, a melhor abordagem provavelmente seria usar as propriedades, se você tiver alguma lógica, provavelmente deverá passar por ela mesma, especialmente se essa lógica for sincronização de encadeamentos.

Mas você também deve levar em consideração que isso pode prejudicar seu desempenho (um pouco); se você estiver sincronizando incorretamente, poderá se trancar e, em algum momento, o caminho correto é contornar a lógica da propriedade.

AK_
fonte
3
Eu também gosto public string MyValue {get; private set;}.
3

Bem, a abordagem direta ao ponto seria atribuí-la apenas à variável em si, já que você está dentro de um método da classe e controla o comportamento da classe.

Mas o ponto principal das propriedades é que elas abstraem a variável. Enquanto uma propriedade tão simples como no seu exemplo não tem nenhum uso completo sobre apenas uma variável de membro pública simples, as propriedades geralmente fazem (ou deveriam fazer) coisas adicionais dentro de seus getters e setters. E se você deseja que essas coisas sejam feitas automaticamente ao alterar a propriedade dentro da classe, é mais claro trabalhar na propriedade, em vez da variável, não precisar alterar cada atribuição de variável quando o comportamento da configuração da propriedade mudar.

Você só precisa raciocinar conceitualmente. A propriedade é realmente um identificador para acessar algum estado interno do objeto, que pode ser composto por mais de uma variável de membro. Portanto, você deve se perguntar se deseja alterar apenas o estado interno subjacente (ou apenas parte dele) ou a propriedade abstrata que representa esse estado como um todo. um estado consistente.

Chris diz Restabelecer Monica
fonte
2

Se houver alguma chance de que a implementação get / set dessa propriedade mude algumas vezes mais tarde (por exemplo, você deseja aumentar um evento ao chamar setou adicionar algum mecanismo de avaliação lento mais tarde à sua getfunção), pode ser uma boa ideia que seu código dentro da classe usará a propriedade em quase todos os casos, exceto nos casos - provavelmente raros - em que você explicitamente não deseja que esses mecanismos de avaliação preguiçosos ou de eventos sejam usados.

De qualquer forma, seja o que for que você faça, há uma boa chance de que, quando você alterar a implementação da propriedade posteriormente dessa maneira, tenha que procurar em todos os lugares da sua classe acessando essas propriedades para verificar se realmente a propriedade será acessada ou o variável privada deve ser usada.

Doc Brown
fonte
2

Eu sempre uso a propriedade pública.

Freqüentemente, alguma lógica que sempre deve ser executada quando a propriedade é configurada é adicionada ao setmétodo de uma propriedade e, ao definir o campo privado, o setter público ignora qualquer lógica existente.

Você tem um comentário sobre o MVVM que leva a essa pergunta e eu sinto que isso é ainda mais importante ao trabalhar com o MVVM. Muitos objetos emitem uma PropertyChangenotificação ao configurador e outros objetos podem se inscrever neste evento para executar alguma ação quando propriedades específicas são alteradas. Se você definir a variável privada, essas ações nunca serão executadas, a menos que você também aumente manualmente o PropertyChangedevento.

Rachel
fonte
+1 Sim na maioria dos casos (MVVM), o evento PropertyChanged é obrigatório. E isso só pode ser demitido dentro de uma propriedade. Boa explicação.
Edward
1

Geralmente, cabe a você o que você deve fazer com uma propriedade e seu campo de apoio ao obter / configurar.

Na maioria das vezes, para ser consistente com o código, você deve usar acessadores públicos sempre que disponíveis e adequados. Isso permite refatorar com alteração mínima do código; se o método que faz essa configuração precisar ser retirado da classe e colocado em outro lugar onde o campo de apoio não estiver mais disponível (como uma classe base), quem se importa? Você está usando algo que está disponível onde quer que a classe faça o trabalho. O campo de suporte, na maioria dos casos, é um detalhe de implementação; ninguém fora da sua classe deve saber que existe.

A principal situação em que consigo pensar quando você deve usar o campo de apoio e NÃO o acessador de propriedade é quando o acessador possui lógica adicional (validação ou atualização de outras informações de estado na classe) que você não deseja executar. A população inicial de um objeto é um exemplo; você pode ter uma classe que usa dois valores de propriedade para calcular um terceiro, que também é armazenado em um campo de suporte (por motivos de persistência). Ao inicializar uma nova cópia desse objeto, dados os dados do banco de dados, os acessadores de propriedades que recalculam o terceiro valor podem reclamar se o outro valor necessário não estiver definido. Ao usar os campos de apoio para definir os valores iniciais dessas duas (ou três) propriedades, você ignora a lógica de validação / cálculo até que a instância esteja em um estado consistente o suficiente para que a lógica funcione normalmente.

KeithS
fonte
0

Sempre use aquele que faz sentido. Sim, eu sei que isso soa muito falso a ponto de não ser uma resposta.

O objetivo das propriedades é fornecer uma interface através da qual você possa acessar com segurança um modelo de dados. Para a maioria das situações, você sempre deseja acessar com segurança o modelo de dados por meio dessa interface, como:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Mas em outras situações, você pode simplesmente estar usando uma propriedade como uma visualização de um modelo de dados:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Se faz sentido usar a forma de radianos SomeAngle, use-a de todos os modos.

No final, não deixe de beber seu próprio kool-aid. Sua API do público deve ser resistente o suficiente para funcionar internamente.

zzzzBov
fonte