Práticas recomendadas de getters, setters e propriedades. Java vs. C #

92

Estou fazendo um curso de C # agora e estou tentando descobrir a melhor maneira de fazer as coisas. Eu venho de uma experiência em Java e, portanto, estou familiarizado apenas com as melhores práticas de Java; Sou um novato em C #!

Em Java, se tenho uma propriedade privada, faço isso;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

Em C #, vejo que existem muitas maneiras de fazer isso.

Posso fazer como Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Ou posso fazer desta forma:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Ou:

public string Name { get; set; }

Qual devo usar e quais são as advertências ou sutilezas envolvidas em cada abordagem? Ao criar classes, estou seguindo as melhores práticas gerais que conheço de Java (especialmente lendo Effective Java). Então, por exemplo, estou favorecendo a imutabilidade (fornecendo setters apenas quando necessário). Estou apenas curioso para ver como essas práticas se encaixam nas várias maneiras de fornecer setters e getters em C #; essencialmente, como traduziria as práticas recomendadas do mundo Java para C #?

EDITAR

Eu estava postando isso como um comentário à resposta de Jon Skeet, mas depois demorou muito:

Que tal uma propriedade não trivial (ou seja, com processamento e validação significativos, talvez)? Eu ainda poderia expô-lo por meio de uma propriedade pública, mas com a lógica encapsulada em gete set? Por que eu faria / deveria fazer isso sobre ter métodos setter e getter dedicados (com processamento associado e lógica de validação).

Vivin Paliath
fonte

Respostas:

88

Pré-C # 6

Eu usaria o último deles, para uma propriedade trivial. Observe que eu chamaria isso de propriedade pública , já que getters e setters são públicos.

Imutabilidade é um pouco chata com propriedades implementadas automaticamente - você não pode escrever uma propriedade automática que tenha apenas um getter; o mais próximo que você pode chegar é:

public string Foo { get; private set; }

o que não é realmente imutável ... apenas imutável fora de sua classe. Portanto, você pode querer usar uma propriedade real somente leitura:

private readonly string foo;
public string Foo { get { return foo; } }

Você definitivamente não quer escrever getName()e setName(). Em alguns casos, faz sentido escrever métodos Get / Set em vez de usar propriedades, especialmente se eles puderem ser caros e você desejar enfatizar isso. No entanto, você deseja seguir a convenção de nomenclatura .NET de PascalCase para métodos e não deseja que uma propriedade trivial como essa seja implementada com métodos normais - uma propriedade é muito mais idiomática aqui.

C # 6

Viva, finalmente temos propriedades adequadas somente leitura automaticamente implementadas:

// This can only be assigned to within the constructor
public string Foo { get; }

Da mesma forma para as propriedades somente leitura que fazer necessidade de fazer algum trabalho, você pode usar propriedades do membro de corpo:

public double Area => height * width;
Jon Skeet
fonte
5
Mais exato: qualquer revisão de código apontará que o método java é um hack que está contornando as construções de tempo de execução AND (!) De linguagem válida e eliminando o uso da propriedade como propriedade (ou seja, object.property = "valor"). Em algumas equipes, isso resultaria em uma boa conversa sobre atitude - dependendo da antiguidade combinada com um incentivo para colocar essa atitude em prática em um competidor. Sério, NÃO LUTE COM O LANGAUGE. Especialmente porque o java "way" é um hack que foi escolhido para não modificar a linguagem de suporte imobiliário.
TomTom
1
Acho que a boa notícia é que minha resposta não parece contradizer nada que você mencionou. A má notícia é que seus dedos são muito mais rápidos que os meus. Grande percepção e obrigado pelos detalhes adicionados.
jeremyalan
4
Para responder à sua edição: você pode usar os métodos get / set com qualquer quantidade de lógica que desejar. Freqüentemente fazemos isso, especialmente para validação. A prática recomendada, entretanto, é não ter muita lógica lenta (acesso ao banco de dados, por exemplo), lógica perigosa (lançamento de exceção) ou mutação (mudar muito de estado) nas propriedades. Espera-se que uma propriedade atue mais ou menos como um estado simples. Qualquer coisa a mais deve ser indicada usando uma função.
CodexArcanum
2
@rtindru: Sim, estou ciente disso. É perfeitamente possível escrever uma propriedade somente leitura. Mas você não pode declarar uma propriedade implementada automaticamente sem um acessador definido.
Jon Skeet
2
A partir do C # 6, agora você pode ter propriedades automáticas sem um acessador definido , ( public int Y { get; } = y;).
Scott Chamberlain
17

Se você só precisa de uma variável para armazenar alguns dados:

public string Name { get; set; }

Quer fazer com que pareça somente leitura?

public string Name { get; private set; }

Ou melhor ainda ...

private readonly string _name;

...

public string Name { get { return _name; } }

Quer fazer alguma verificação de valor antes de atribuir a propriedade?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Em geral, GetXyz () e SetXyz () são usados ​​apenas em certos casos, e você só precisa usar sua intuição quando achar que está certo. Em geral, eu diria que espero que a maioria das propriedades get / set não contenha muita lógica e tenha poucos, se houver, efeitos colaterais inesperados. Se a leitura de um valor de propriedade requer a chamada de um serviço ou a obtenção de entrada de um usuário para construir o objeto que estou solicitando, eu o envolveria em um método e o chamaria de algo como BuildXyz(), em vez de GetXyz().

jeremyalan
fonte
2
Eu não lançaria exceções nas propriedades, prefiro usar setters de método com contratos para especificar esse comportamento específico. Se uma propriedade for do tipo int, espero que todos os int se qualifiquem. Algo que exige o lançamento de exceção não é simples na minha opinião, invocações de tipo INotifyPropertyChanged estão mais nessa linha de acordo com mim.
flindeberg
3
setter privado! = imutável
piedar
piedar está certo. Um setter privado significa apenas que não posso fazer atribuições, mas ainda posso usar myList.Add(), por exemplo (desde que o objeto seja exposto a mudanças, ele é mutável).
1
@flindeberg Desculpe, mas eu discordo ... E se você tiver uma barra de progresso com um valor Min/ Max. Você quer garantir isso Max > Min? Ter um SetRange(Min, Max)pode fazer sentido, mas como você vai ler esses valores de volta? Propriedades mínimas / máximas somente leitura? Lançar exceções para entrada inválida parece ser a maneira mais limpa de lidar com isso.
Básico de
12

Use propriedades em C #, não métodos get / set. Eles estão aí para sua conveniência e isso é idiomático.

Quanto aos seus dois exemplos de C #, um é simplesmente açúcar sintático para o outro. Use a propriedade auto se tudo que você precisar for um invólucro simples em torno de uma variável de instância, use a versão completa quando precisar adicionar lógica no getter e / ou setter.

Ed S.
fonte
5

Em C #, favorece as propriedades para expor campos privados para get e / ou set. A forma que você mencionou é uma propriedade autônoma em que get e set geram automaticamente um campo de suporte de pivô oculto para você.

Eu prefiro propriedades automáticas quando possível, mas você nunca deve fazer um par de métodos set / get em C #.

James Michael Hare
fonte
5
public string Name { get; set; }

Esta é simplesmente uma propriedade implementada automaticamente e é tecnicamente igual a uma propriedade normal. Um campo de apoio será criado durante a compilação.

Todas as propriedades são eventualmente convertidas em funções, portanto, a implementação real compilada no final é a mesma que você está acostumado em Java.

Use as propriedades implementadas automaticamente quando não precisar fazer operações específicas no campo de apoio. Caso contrário, use uma propriedade comum. Use as funções get e set quando a operação tiver efeitos colaterais ou for computacionalmente cara; caso contrário, use as propriedades.

Steven Jeuris
fonte
4

Independentemente de qual forma você escolher em C #, o resultado final é o mesmo. Você obterá uma variável backinng com métodos getter e setter separados. Ao usar propriedades, você está seguindo as práticas recomendadas e, portanto, é uma questão de quão detalhado você deseja obter.

Pessoalmente, eu escolheria propriedades automáticas, a última versão:, public string Name { get; set; }uma vez que ocupam menos espaço. E você sempre pode expandi-los no futuro se precisar adicionar algo como validação.

Paul Sasik
fonte
4

Sempre que possível, prefiro o público string Name { get; set; }porque é conciso e de fácil leitura. No entanto, pode haver momentos em que este seja necessário

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}
SquidScareMe
fonte
2
você pode explicar quando e por que esse tipo de coisa é necessária?
Jesper
No momento, não consigo pensar em uma razão para usar a 2ª versão. Eu apenas disse 'pode haver momentos em que isso seja necessário'. Odeio usar absolutos, a menos que seja positivo.
SquidScareMe de
3
E por que isso é 'lolz'? Você pode mostrar seu apoio a um comentário votando positivamente.
SquidScareMe de
1
Uma razão para usar um campo de apoio explícito é quando você deseja fazer operações atômicas / thread-safe em relação ao valor
Básico,
4

Em C #, a forma preferida é por meio de propriedades em vez de métodos getX()e setX(). Além disso, observe que o C # não requer que as propriedades tenham get e set - você pode ter propriedades get-only e propriedades set-only.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}
Zach Johnson
fonte
4

Primeiro, deixe-me tentar explicar o que você escreveu:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Esta estrutura também é usada hoje em dia, mas é mais adequada se você quiser fazer alguma funcionalidade extra, por exemplo, quando um valor é definido, você pode analisá-lo para capitalizá-lo e salvá-lo em membro privado para alterar o uso interno.

Com .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Desejo-lhe melhor sorte em C #

Waqas Raja
fonte
3

Conforme mencionado, todas essas abordagens resultam no mesmo resultado. O mais importante é que você escolha uma convenção e a siga. Prefiro usar os dois últimos exemplos de propriedades.

Jeff LaFay
fonte
2

como a maioria das respostas aqui, use as propriedades Automáticas. Intuitivo, menos linhas de código e mais limpo. Se você deve serializar sua classe, marque a classe [Serializable]/ com o [DataConract]atributo. E se você estiver usando, [DataContract]marque o membro com

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

O setter privado ou público depende da sua preferência.

Observe também que as propriedades automáticas requerem getters e setters (públicos ou privados).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Alternativamente, se você deseja apenas obter / definir, crie você mesmo um campo de apoio

RAM
fonte
0

Qual devo usar e quais são as advertências ou sutilezas envolvidas em cada abordagem?

Ao usar propriedades, há uma advertência que ainda não foi mencionada: com propriedades, você não pode ter nenhuma parametrização de seus getters ou setters.

Por exemplo, imagine que você deseja recuperar os itens de uma lista e também aplicar um filtro ao mesmo tempo. Com um método get, você pode escrever algo como:

obj.getItems(filter);

Em contraste, com uma propriedade, você é forçado a primeiro devolver todos os itens

obj.items

e, em seguida, aplique o filtro na próxima etapa ou você terá que adicionar propriedades dedicadas que expõem itens filtrados por critérios diferentes, o que logo sobrecarrega sua API:

obj.itemsFilteredByX
obj.itemsFilteredByY

O que às vezes pode ser um incômodo é quando você começou com uma propriedade, por exemplo, obj.itemse depois descobriu que a parametrização getter ou setter é necessária ou tornaria as coisas mais fáceis para o usuário da API de classe. Agora você precisaria reescrever sua API e modificar todos os locais em seu código que acessam essa propriedade ou encontrar uma solução alternativa. Em contraste, com um método get, por exemplo obj.getItems(), você pode simplesmente estender a assinatura do seu método para aceitar um objeto de "configuração" opcional, por exemplo, obj.getItems(options)sem ter que reescrever todos aqueles lugares que chamam seu método.

Dito isso, as propriedades (auto-implementadas) em C # ainda são atalhos muito úteis (pelas várias razões mencionadas aqui), já que na maioria das vezes a parametrização pode não ser necessária - mas esta advertência permanece.

B12Toaster
fonte