Em seu excelente livro, CLR Via C #, Jeffrey Richter disse que não gosta de propriedades e recomenda não usá-las. Ele deu algum motivo, mas eu realmente não entendo. Alguém pode me explicar por que devo ou não usar propriedades? No C # 3.0, com propriedades automáticas, isso muda?
Como referência, adicionei as opiniões de Jeffrey Richter:
• Uma propriedade pode ser somente leitura ou somente gravação; o acesso ao campo é sempre legível e gravável. Se você definir uma propriedade, é melhor oferecer os métodos de acesso get e set.
• Um método de propriedade pode lançar uma exceção; o acesso de campo nunca lança uma exceção.
• Uma propriedade não pode ser passada como parâmetro out ou ref para um método; um campo pode. Por exemplo, o código a seguir não será compilado:
using System;
public sealed class SomeType
{
private static String Name
{
get { return null; }
set {}
}
static void MethodWithOutParam(out String n) { n = null; }
public static void Main()
{
// For the line of code below, the C# compiler emits the following:
// error CS0206: A property or indexer may not
// be passed as an out or ref parameter
MethodWithOutParam(out Name);
}
}
• Um método de propriedade pode levar muito tempo para ser executado; o acesso ao campo sempre é concluído imediatamente. Um motivo comum para usar propriedades é executar a sincronização do thread, o que pode interromper o thread para sempre e, portanto, uma propriedade não deve ser usada se a sincronização do thread for necessária. Nessa situação, um método é preferido. Além disso, se sua classe pode ser acessada remotamente (por exemplo, sua classe é derivada de System.MashalByRefObject), chamar o método de propriedade será muito lento e, portanto, um método é preferível a uma propriedade. Na minha opinião, as classes derivadas de MarshalByRefObject nunca devem usar propriedades.
• Se chamado várias vezes em uma linha, um método de propriedade pode retornar um valor diferente a cada vez; um campo retorna o mesmo valor todas as vezes. A classe System.DateTime tem uma propriedade Now somente leitura que retorna a data e hora atuais. Cada vez que você consultar essa propriedade, ela retornará um valor diferente. Isso é um erro, e a Microsoft deseja poder consertar a classe tornando o Now um método em vez de uma propriedade.
• Um método de propriedade pode causar efeitos colaterais observáveis; o acesso ao campo nunca o faz. Em outras palavras, um usuário de um tipo deve ser capaz de definir várias propriedades definidas por um tipo em qualquer ordem que ele escolher, sem perceber qualquer comportamento diferente no tipo.
• Um método de propriedade pode requerer memória adicional ou retornar uma referência a algo que não é realmente parte do estado do objeto, portanto, modificar o objeto retornado não tem efeito sobre o objeto original; a consulta de um campo sempre retorna uma referência a um objeto que é garantido como parte do estado do objeto original. Trabalhar com uma propriedade que retorna uma cópia pode ser muito confuso para os desenvolvedores e essa característica freqüentemente não é documentada.
fonte
Respostas:
A razão de Jeff para não gostar de propriedades é porque elas se parecem com campos - então os desenvolvedores que não entendem a diferença irão tratá-las como se fossem campos, presumindo que elas sejam baratas de executar, etc.
Pessoalmente, discordo dele neste ponto específico - acho que as propriedades tornam o código do cliente muito mais simples de ler do que as chamadas de método equivalentes. Concordo que os desenvolvedores precisam saber que as propriedades são basicamente métodos disfarçados - mas acho que educar os desenvolvedores sobre isso é melhor do que tornar o código mais difícil de ler usando métodos. (Em particular, tendo visto o código Java com vários getters e setters sendo chamados na mesma instrução, eu sei que o código C # equivalente seria muito mais simples de ler. A Lei de Demeter é muito boa em teoria, mas às vezes
foo.Name.Length
realmente é a coisa certa a usar ...)(E não, as propriedades implementadas automaticamente não mudam nada disso.)
Isso é um pouco como os argumentos contra o uso de métodos de extensão - posso entender o raciocínio, mas o benefício prático (quando usado com moderação) supera o lado negativo, na minha opinião.
fonte
Bem, vamos considerar seus argumentos um por um:
Isso é uma vitória para as propriedades, pois você tem um controle de acesso mais refinado.
Embora isso seja verdade, você pode muito bem chamar um método em um campo de objeto não inicializado e ter uma exceção lançada.
Justo.
Também pode demorar muito pouco tempo.
Não é verdade. Como você sabe que o valor do campo não mudou (possivelmente por outro segmento)?
Se for um erro, é um pequeno erro.
Justo.
Muitos dos protestos poderiam ser feitos para os getters e setters de Java também - e nós os tivemos por um bom tempo sem tais problemas na prática.
Acho que a maioria dos problemas poderia ser resolvida com um melhor realce de sintaxe (ou seja, diferenciando propriedades de campos) para que o programador saiba o que esperar.
fonte
_field
. Ou apenas em minúsculasfield
.Não li o livro e você não citou a parte que não entende, então terei de adivinhar.
Algumas pessoas não gostam de propriedades porque podem fazer seu código fazer coisas surpreendentes.
Se eu digitar
Foo.Bar
, as pessoas que lêem normalmente esperam que isso seja simplesmente acessando um campo membro da classe Foo. É uma operação barata, quase gratuita e determinística. Posso chamá-lo repetidamente e obter o mesmo resultado todas as vezes.Em vez disso, com propriedades, pode realmente ser uma chamada de função. Pode ser um loop infinito. Pode abrir uma conexão de banco de dados. Ele pode retornar valores diferentes sempre que eu o acessar.
É um argumento semelhante ao motivo pelo qual Linus odeia C ++. Seu código pode surpreender o leitor. Ele odeia sobrecarga de operador:
a + b
não significa necessariamente adição simples. Pode significar alguma operação extremamente complicada, assim como as propriedades do C #. Pode ter efeitos colaterais. Pode fazer qualquer coisa.Honestamente, acho que esse é um argumento fraco. Ambas as línguas estão cheias de coisas assim. (Devemos evitar a sobrecarga de operadores em C # também? Afinal, o mesmo argumento pode ser usado lá)
Propriedades permitem abstração. Podemos fingir que algo é um campo regular e usá-lo como se fosse um, sem precisar nos preocupar com o que está acontecendo nos bastidores.
Isso geralmente é considerado uma coisa boa, mas obviamente depende do programador escrever abstrações significativas. Suas propriedades devem se comportar como campos. Eles não devem ter efeitos colaterais, eles não devem realizar operações caras ou inseguras. Queremos ser capazes de pensar neles como campos.
No entanto, tenho outro motivo para considerá-los menos que perfeitos. Eles não podem ser passados por referência a outras funções.
Os campos podem ser passados como
ref
, permitindo que uma função chamada acesse-os diretamente. As funções podem ser passadas como delegados, permitindo que uma função chamada acesse-a diretamente.Propriedades ... não posso.
Isso é péssimo.
Mas isso não significa que as propriedades sejam más ou não devam ser usadas. Para muitos propósitos, eles são ótimos.
fonte
ref
parâmetros; um membro (por exemploFoo
) de um formuláriovoid Foo<T>(ActionByRef<Point,T> proc, ref T param)
com um atributo especial e tem a transformação do compiladorthing.Foo.X+=someVar;
emFoo((ref Point it, ref int param)=>it.X += param, ref someVar);
. Como o lambda é um delegado estático, nenhum fechamento seria necessário e o usuário de um objeto poderia usar qualquer local de armazenamento que apoiasse a propriedade como umref
parâmetro genuíno (e poderia passá-lo para outros métodos como umref
parâmetro).Em 2009, esse conselho parecia apenas uma dor de barriga da variedade de Quem Mexeu no Meu Queijo . Hoje, está quase ridiculamente obsoleto.
Um ponto muito importante que muitas respostas parecem errar, mas não abordam de frente é que esses supostos "perigos" das propriedades são uma parte intencional do design da estrutura!
Sim, as propriedades podem:
Especifique diferentes modificadores de acesso para getter e setter. Esta é uma vantagem sobre os campos. Um padrão comum é ter um getter público e um setter protegido ou interno , uma técnica de herança muito útil que não pode ser alcançada apenas pelos campos.
Lance uma exceção. Até o momento, este continua sendo um dos métodos mais eficazes de validação, especialmente ao trabalhar com estruturas de IU que envolvem conceitos de vinculação de dados. É muito mais difícil garantir que um objeto permaneça em um estado válido ao trabalhar com campos.
Demora muito para executar. A comparação válida aqui é com métodos , que demoram igualmente - não campos . Nenhuma base é dada para a afirmação "um método é preferido", exceto a preferência pessoal de um autor.
Retorna valores diferentes de seu getter nas execuções subsequentes. Isso quase parece uma piada tão perto do ponto de exaltar as virtudes dos parâmetros
ref
/out
com campos, cujo valor de um campo após uma chamadaref
/out
é praticamente garantido ser diferente de seu valor anterior, e de forma imprevisível.Se estamos falando sobre o caso específico (e praticamente acadêmico) de acesso de thread único sem acoplamentos aferentes, é bastante entendido que é apenas um projeto de propriedade ruim ter efeitos colaterais de mudança de estado visível, e talvez minha memória esteja desaparecendo, mas simplesmente não consigo me lembrar de nenhum exemplo de pessoas usando
DateTime.Now
e esperando o mesmo valor sempre. Pelo menos não em qualquer caso em que eles não teriam estragado tudo tão mal com uma hipotéticaDateTime.Now()
.Causam efeitos colaterais observáveis - que é, obviamente, precisamente a razão pela qual as propriedades foram inventadas como um recurso de linguagem em primeiro lugar. As próprias diretrizes de design de propriedade da Microsoft indicam que a ordem do setter não deve importar, pois fazer o contrário implicaria em acoplamento temporal . Certamente, você não pode alcançar o acoplamento temporal com os campos sozinho, mas isso apenas porque você não pode causar nenhum comportamento significativo para acontecer com os campos sozinho, até que algum método seja executado.
Os acessadores de propriedade podem ajudar a prevenir certos tipos de acoplamento temporal, forçando o objeto a um estado válido antes que qualquer ação seja executada - por exemplo, se uma classe tem um
StartDate
e umEndDate
, então definir oEndDate
antes de tambémStartDate
pode forçar oStartDate
retorno. Isso é verdadeiro mesmo em ambientes multithread ou assíncronos, incluindo o exemplo óbvio de uma interface de usuário orientada a eventos.Outras coisas que as propriedades podem fazer e os campos não podem incluir:
Type
ouName
derivadas pode fornecer metadados interessantes, mas constantes sobre si mesmas.Item(i)
chamadas, reconhecerão como algo maravilhoso.Richter é claramente um autor prolífico e sabe muito sobre CLR e C #, mas devo dizer que parece que foi quando ele escreveu este conselho originalmente (não tenho certeza se está em suas revisões mais recentes - sinceramente espero que não) que ele simplesmente não queria abandonar os velhos hábitos e estava tendo problemas para aceitar as convenções do C # (vs. C ++, por exemplo).
O que quero dizer com isso é que seu argumento de "propriedades consideradas prejudiciais" essencialmente se resume a uma única declaração: as propriedades parecem campos, mas podem não agir como campos. E o problema com a afirmação é que não é verdade ou, na melhor das hipóteses, é altamente enganosa. Propriedades não se parecem com campos - pelo menos, elas não deveriam se parecer com campos.
Existem duas convenções de codificação muito fortes em C # com convenções semelhantes compartilhadas por outras linguagens CLR, e FXCop gritará com você se você não segui-las:
Assim, não há ambigüidade sobre se
Foo.Bar = 42
é um acessador de propriedade ou um acessador de campo. É um acessador de propriedade e deve ser tratado como qualquer outro método - pode ser lento, pode lançar uma exceção, etc. Essa é a natureza de Abstraction - fica inteiramente a critério da classe declarante como reagir. Os projetistas de classe devem aplicar o princípio da menor surpresa, mas os chamadores não devem presumir nada sobre uma propriedade, exceto que ela faz o que diz na lata. Isso é de propósito.A alternativa às propriedades são os métodos getter / setter em todos os lugares. Essa é a abordagem Java e tem sido controversa desde o início . Tudo bem se essa for a sua, mas não é assim que atuamos no campo .NET. Tentamos, pelo menos dentro dos limites de um sistema estaticamente tipado, evitar o que Fowler chama de ruído sintático . Não queremos parênteses extras, extras
get
/set
verrugas ou assinaturas de métodos extras - não se pudermos evitá-los sem qualquer perda de clareza.Diga o que quiser, mas
foo.Bar.Baz = quux.Answers[42]
sempre será muito mais fácil de ler do quefoo.getBar().setBaz(quux.getAnswers().getItem(42))
. E quando você está lendo milhares de linhas disso por dia, faz diferença.(E se a sua resposta natural ao parágrafo acima for dizer, "claro que é difícil de ler, mas seria mais fácil se você dividisse em várias linhas", então lamento dizer que você perdeu completamente o ponto .)
fonte
Não vejo nenhum motivo pelo qual você não deve usar Propriedades em geral.
As propriedades automáticas em C # 3+ apenas simplificam um pouco a sintaxe (a la açúcar sintático).
fonte
É a opinião de apenas uma pessoa. Eu li alguns livros c # e ainda não vi ninguém dizendo "não use propriedades".
Pessoalmente, acho que as propriedades são uma das melhores coisas sobre c #. Eles permitem que você exponha o estado por meio de qualquer mecanismo que desejar. Você pode instanciar preguiçosamente a primeira vez que algo é usado e pode fazer a validação na definição de um valor, etc. Ao usá-los e gravá-los, eu apenas penso nas propriedades como setters e getters, que são uma sintaxe muito mais agradável.
Quanto às ressalvas com propriedades, há algumas. Provavelmente, um deles é o uso indevido de propriedades, o outro pode ser sutil.
Em primeiro lugar, propriedades são tipos de métodos. Pode ser surpreendente se você colocar uma lógica complicada em uma propriedade porque a maioria dos usuários de uma classe espera que a propriedade seja bastante leve.
Por exemplo
Acho que usar métodos para esses casos ajuda a fazer uma distinção.
Em segundo lugar, e mais importante, as propriedades podem ter efeitos colaterais se você avaliá-las durante a depuração.
Digamos que você tenha alguma propriedade como esta:
Agora, suponha que você tenha uma exceção que ocorre quando muitas consultas são feitas. Adivinhe o que acontece quando você inicia a depuração e faz o rollover da propriedade no depurador? Coisas ruins. Evite fazer isso! Olhar para a propriedade muda o estado do programa.
Estas são as únicas ressalvas que tenho. Acho que os benefícios das propriedades superam em muito os problemas.
fonte
Essa razão deve ter sido dada dentro de um contexto muito específico. Geralmente é o contrário - é recomendado usar propriedades, pois elas fornecem um nível de abstração que permite alterar o comportamento de uma classe sem afetar seus clientes ...
fonte
Não posso deixar de insistir nos detalhes das opiniões de Jeffrey Richter:
Errado: os campos podem ser marcados como somente leitura para que apenas o construtor do objeto possa gravar neles.
Errado: a implementação de uma classe pode alterar o modificador de acesso de um campo de público para privado. As tentativas de ler campos privados em tempo de execução sempre resultarão em uma exceção.
fonte
Não concordo com Jeffrey Richter, mas posso imaginar por que ele não gosta de propriedades (não li seu livro).
Mesmo assim, as propriedades são como métodos (em termos de implementação), como um usuário de uma classe, espero que suas propriedades se comportem "mais ou menos" como um campo público, por exemplo:
Infelizmente, tenho visto propriedades que não se comportam dessa forma. Mas o problema não são as propriedades em si, mas as pessoas que as implementaram. Portanto, requer apenas um pouco de educação.
fonte
Há um momento em que considero não usar propriedades, e isso ocorre ao escrever o código do .Net Compact Framework. O compilador CF JIT não executa a mesma otimização que o compilador JIT de desktop e não otimiza acessadores de propriedade simples, portanto, neste caso, adicionar uma propriedade simples causa um pequeno volume de código sobre o uso de um campo público. Normalmente, isso não seria um problema, mas quase sempre no mundo do Compact Framework você enfrenta fortes restrições de memória, portanto, mesmo pequenas economias como essa contam.
fonte
Você não deve evitar usá-los, mas deve usá-los com qualificação e cuidado, pelas razões apresentadas por outros contribuidores.
Certa vez, vi uma propriedade chamada algo como Clientes que abriu internamente uma chamada fora do processo para um banco de dados e leu a lista de clientes. O código do cliente tinha um 'for (int i to Customers.Count)' que estava causando uma chamada separada para o banco de dados em cada iteração e para o acesso do Cliente selecionado. Esse é um exemplo flagrante que demonstra o princípio de manter a propriedade muito leve - raramente mais do que um acesso de campo interno.
Um argumento para o uso de propriedades é que elas permitem que você valide o valor que está sendo definido. Outra é que o valor de da propriedade pode ser um valor derivado, não um único campo, como TotalValue = quantidade * quantidade.
fonte
Pessoalmente, só uso propriedades ao criar métodos get / set simples. Eu me afasto disso quando chego a estruturas de dados complicadas.
fonte
O argumento pressupõe que as propriedades são ruins porque se parecem com campos, mas podem realizar ações surpreendentes. Esta suposição é invalidada pelas expectativas dos programadores .NET:
As propriedades não se parecem com campos. Os campos parecem propriedades.
Portanto, um campo é como uma propriedade que nunca lançará uma exceção.
Portanto, um campo é como uma propriedade, mas tem recursos adicionais: passar para métodos a
ref
/out
accept.Portanto, um campo é como uma propriedade rápida.
Portanto, um campo é como uma propriedade que tem garantia de retornar o mesmo valor, a menos que o campo tenha sido definido com um valor diferente.
Novamente, um campo é uma propriedade que certamente não fará isso.
Este pode ser surpreendente, mas não porque é uma propriedade que faz isso. Em vez disso, quase ninguém retorna cópias mutáveis em propriedades, de modo que 0,1% do caso é surpreendente.
fonte
Chamar métodos em vez de propriedades reduz muito a legibilidade do código de chamada. Em J #, por exemplo, usar ADO.NET era um pesadelo porque Java não oferece suporte a propriedades e indexadores (que são essencialmente propriedades com argumentos). O código resultante era extremamente feio, com chamadas de método de parênteses vazias por toda parte.
O suporte para propriedades e indexadores é uma das vantagens básicas do C # em relação ao Java.
fonte