Por que os prefixos nos campos são desencorajados?

19

Nos velhos tempos, fizemos a notação húngara. Agora isso é considerado passé e, na maioria das vezes, não o uso mais, mas ainda acho útil o m_prefixo para indicar os campos dos membros.

Para mim, se estou lendo o código de outra pessoa e vejo o seguinte:

count = 3;

Suponho que countseja uma variável local para essa função e estou fazendo algo que será usado em outro lugar na função; se eu vir isso:

m_count = 3;

Percebo imediatamente que estou atualizando o estado do objeto.

As diretrizes de estilo da Microsoft dizem que essa é a maneira errada de fazer as coisas. Devo nomear meus campos não públicos exatamente como variáveis ​​temporárias definidas dentro da função. Não m_, nem mesmo um simples sublinhado.

Sim, podemos definir nosso próprio estilo de codificação, mas então eu tenho que lutar com várias ferramentas de análise de código estáticas, para convencê-las de que nossa maneira de fazer as coisas é boa.

Estou feliz em mudar para o estilo da Microsoft, mas gostaria de saber por que as coisas são do jeito que são.

Por que agora é considerado ruim poder saber se uma variável é função local ou membro?

PS Isso é muito parecido com o que são as melhores práticas atuais consideradas em relação à palavra-chave "this" na frente do campo e métodos em c #? , mas estou perguntando sobre m_nãothis.

PPS Consulte também Por que a Microsoft criou parâmetros, variáveis ​​locais e campos privados com a mesma convenção de nomenclatura?

Betty Crokker
fonte
9
C # não tem a thispalavra - chave? Você não pode se referir a membros usando this, como this.count?
FrustratedWithFormsDesigner
3
O prefixo m_ é um iso em C ++, onde evita conflitos de nomes entre campos e métodos. No C #, isso não é um problema, porque os nomes dos métodos devem começar com maiúsculas, campos com minúsculas.
amon
4
Se você estiver imprimindo código em 2017, estará fazendo algo errado. Nos outros dois cenários, deve ser bastante óbvio que countnão apareceu do nada, pois não foi declarado no método. Francamente, o argumento "fora do IDE" é MUITO fraco. Especialmente porque existem até ferramentas de revisão de código que funcionam no IDE.
Andy
4
Post ainda relevante de Joel .
Kasey Speakman

Respostas:

19

Quando eles estavam trabalhando na API para Windows, a Microsoft recomendou o uso da notação húngara, usando 'm_' e 'i', 'sz' etc. Na época, isso era útil porque o IDE não era robusto. o suficiente para mostrar essas informações para você.

O C #, por outro lado, tem um IDE muito robusto desde o início.

Portanto, eles fizeram a recomendação nas Diretrizes de Nomenclatura da Microsoft para C # para campos públicos estáticos ou protegidos:

 DO use PascalCasing in field names.
 DO name fields using a noun, noun phrase, or adjective.
X DO NOT use a prefix for field names.

Agora que você pode passar o mouse sobre ele com o mouse ou pressionar CTRL + K + W e obter todas as informações sobre o membro, a Microsoft chegou à conclusão de que os prefixos são incorretos; eles são uma solução manual para um problema já resolvido.

Os campos particulares são especificamente excluídos das diretrizes, mas a Microsoft parece ter chegado à conclusão de que esse é o caso mesmo dos campos particulares daqui para frente internamente, que você pode ver se apontar o Resharper para o .NET 4.5 ou .NET Core.

Use suas ferramentas, não faça manualmente.

Kevin Fee
fonte
2
A mesma resposta que Andy ... sim, mas ... há muitas situações em que estou vendo código fora do IDE. Exemplos - (1) ferramentas de mesclagem. (2) ferramentas de revisão de código. (3) impressões (ofegantes).
Betty Crokker
Andy postou literalmente no mesmo instante que eu. Vi o "1 novo comentário" aparecer ao clicar em "adicionar resposta". Quanto a essas ferramentas, duas delas já estão no IDE. Impressões ... por que você está fazendo isso?
Kevin Fee
1
Minha pergunta inicial permanece sem resposta ... sim, a Microsoft disse que não use m_ e os fabricantes de ferramentas seguiram isso, mas essa não é uma razão técnica para usar m_, é o que é frequentemente chamado de "apelo à autoridade". A única razão técnica que eu vi para não usar m_ veio de Timothy Truckle e eu não entendo ...
Betty Crokker
8
@BettyCrokker Porque m_ não agrega absolutamente nenhum valor. O IDE informa a você membro ou não, portanto o m_ é redundante. Por que você está fixado em m_? Por que não l_ (para local)? Por que não afsdfjdskfjas_? Eu vou rebaixar a questão, porque é um argumento religioso bobo de qualquer maneira. Faça o que você quiser. As diretrizes que você está pressionando também dizem "As diretrizes de nomeação de campos se aplicam a campos públicos e protegidos estáticos . Campos internos e privados não são cobertos por diretrizes , e campos de instância públicos ou protegidos não são permitidos pelas diretrizes de design dos membros."
Andy
1
@BettyCrokker OK, mas você está perguntando sobre as diretrizes feitas com essa suposição. Você também está reclamando de ferramentas estáticas que provavelmente também estão usando essa suposição, porque "ninguém" (dentro de uma margem de erro estatística aceitável) imprime seu código C #. Não sei quais ferramentas de análise estatística você usa, mas o Resharper é bastante específico: os campos particulares são "_lowerCase". Os membros públicos de qualquer tipo ou "UpperCase" e os locais são "lowerCase". Economizar no "m" realmente parece muito bom, de qualquer maneira.
Kevin Fee
17

O C # não possui nenhuma variável global, portanto, resta apenas variáveis ​​locais e campos de classe.

E se você tem tantas variáveis ​​locais que perde o controle de saber se um identificador é um, certamente violou uma das diretrizes para gerenciar a complexidade com mais gravidade.

Tente extrair um objeto-parâmetro, tente dividir sua função em várias mais simples, você certamente deve refatorar seu código, a menos que queira se afogar em minúcias e complexidade, além de não ter consideração pela manutenção.

Agora que estabelecemos que o objetivo das decorações "este é um membro" não é mais válido, pois o escopo da função deve ser muito pequeno e o escopo global não existe, há uma desvantagem nesses marcadores: eles inibem movendo argumentos / variáveis ​​locais para membros ou vice-versa.

Desduplicador
fonte
Depois de pontuar, também pode ser uma classe ou espaço para nome.
CodesInChaos
3
Eu não acho que isso realmente responda à pergunta.
Vladimir Stokic
5
@FrankHileman Na verdade, é. Explica por que não há boas razões para uglificar um nome.
Deduplicator
2
"feio"? Se for esse o caso, é uma pergunta apenas de opinião. Todos podem ter uma opinião diferente sobre a feiúra. Há razões para preferir uma convenção a outra, mas não há convenção padrão nesse caso.
precisa
1
A beleza do @Duplicator está nos olhos de quem vê. Convenções que antes eu considerava feias, agora aprecio como úteis.
precisa
11

Por que os desenvolvedores evitam anexar namespaces à diretiva using namespace?

System.DateTime()é muito mais explícito que DateTime(). Isso me diz exatamente com o que estou lidando. É apenas porque somos datilógrafos preguiçosos?

Não. Somos datilógrafos preguiçosos, mas não é por isso.

Preferimos estilos de codificação que nos permitem ignorar detalhes e focar em abstrações. Forçar nomes a comunicar detalhes da estrutura é uma distração. Quando estou trabalhando em um algoritmo complicado, não quero nomes que insistem em me lembrar todos os pequenos detalhes sobre o que estou trabalhando. Eu só preciso do nome para me impedir de confundir Timere DateTime. Vou me preocupar se eles são compatíveis em outro lugar.

É a mesma razão que você não quer dois caras no seu time chamado Jon. O fato de terem sobrenomes não é um motivo para fornecer prefixos ou sufixos. Só vou te chamar de Skeet. Qualquer coisa mais é uma dor plana.

candied_orange
fonte
2
Isso não parece responder à pergunta. Não há preferência ou convenção geral para campos particulares no .net.
precisa
12
@FrankHileman Em geral, preferimos bons nomes. Convenções não criam bons nomes. As convenções criam nomes como McOhPlease Von StopItAlreadyStine.
candied_orange
As convenções são apenas para facilitar o trabalho com código de desenvolvedor diferente. Sua resposta aborda o húngaro, mas não há convenção para campos particulares, portanto a pergunta é baseada em uma suposição falsa.
21817 Frank Hileman #
7

Vamos expandir um pouco.

Existem três estilos de prefixo comuns, todos encontrados ocasionalmente no código c #:

  • m_
  • _
  • esta.

Esses prefixos são comumente usados ​​na implementação privada de uma classe . A recomendação da Microsoft é principalmente sobre as partes expostas de uma classe.

A vantagem de usar m_over _é que o prefixo pode ser o mesmo em toda a base de código, se você usar c # e também idiomas nos quais o uso _como prefixo não é permitido . * A vantagem de m_over this.é que é mais curto e que você não acidentalmente esqueça o prefixo.

A vantagem do _over m_é que ele é mais curto e que tudo que começa com o prefixo é classificado na parte superior é classificado em ordem alfabética por uma ferramenta. A vantagem do _over this.é que ele é mais curto e você não esquece acidentalmente o prefixo.

A vantagem de this.over m_e _é que você pode usá-lo em uma base de código onde _ou m_não é uma convenção universal e aceita. Isso também significa que ninguém precisa saber sobre o _ou m_convenção, que pode ser visto como fazer as coisas mais simples. Como desvantagem, como o compilador não se importa com esse prefixo, this.ocasionalmente ele estará ausente e, portanto, não será tão confiável quanto um indicador.


* Quando você começa a acessar as classes C # que usam _C ++ / CLI (que realmente não deveriam usar _), você perceberá que concordar com um prefixo comum é mais complexo do que deveria. Decidir não ter um prefixo se livra desse ruído. Combine isso com a resposta do Deduplicator, que parafrasharei como "a vantagem de um prefixo é quase insignificante" e nos fornece: "Há um pequeno problema ao usar prefixos e uma pequena vantagem, portanto, é uma opção válida, mas não apenas usa-os".


uma pergunta mais antiga no stackoverflow relacionada a isso.

Peter - Unban Robert Harvey
fonte
1
Boa comparação. No entanto, acho que não usar nenhum prefixo funciona bem na prática.
Phil1970
6

Deve-se observar que o guia de estilo da MS fala apenas sobre métodos e variáveis ​​públicos e protegidos. ou seja, os outros desenvolvedores verão se eles usam sua biblioteca.

A idéia é que as bibliotecas da Microsoft tenham uma aparência padrão dos desenvolvedores que as consomem.

Você é livre para usar o estilo que quiser nas suas variáveis ​​privadas ou locais.

Dito isto, os prefixos variáveis ​​são desencorajados em geral por várias razões

  • Eles podem estar errados e, portanto, enganosos
  • Se você está prefixando por tipo de variável, não está claro como você lida com as classes que você criou.
  • Existem várias convenções e elas nem sempre concordam. O que você acha útil outro desenvolvedor pode achar inútil
  • No caso de variáveis ​​de membro, você pode usar este 'isto'. palavra-chave para indicar o escopo e o cumpridor o aplicará.
Ewan
fonte
2
Originalmente, eu não usava nenhum prefixo para os campos. Mais tarde, mudei para o uso de um único caractere "_", pois vi outros usuários, porque ele move todos os campos para o topo da lista de membros em ordem alfabética quando você usa as caixas de listagem suspensa IDE. A falta de uma convenção padrão pode ser irritante ao ler código escrito por muitos desenvolvedores diferentes.
precisa
depois de um tempo, você vê tantos estilos diferentes que simplesmente os ignora. Se você tentou impor um estilo que você iria ser constantemente refatoração tudo
Ewan
4
Se você aplicar "Eles podem estar errados e, portanto, enganosos" para quase tudo, você descobrirá rapidamente que não deve nomear variáveis ​​ou funções - elas podem estar erradas, afinal! E eu estou supondo que seu segundo ponto deve dizer "classes que você NÃO criou"? Ou então, simplesmente não entendo ... De qualquer forma, está começando a parecer que ter um único prefixo de sublinhado para campos não públicos é um padrão não oficial, provavelmente essa é a direção que vamos seguir.
Betty Crokker
algumas coisas são verificadas pelo compilador, portanto m_count pode não ser uma variável de membro, mas this.count sempre será
Ewan
muitas pessoas usam _, mas as coisas mudam com o tempo, você descobrirá que o código escrito para 'melhores práticas' no ano passado não corresponde às convenções deste ano
Ewan
4

Para mim, se estou lendo o código de outra pessoa e vejo o seguinte:

count = 3;

Suponho que 'count' seja uma variável local para essa função e estou fazendo algo que será usado em outro lugar na função

E você estará absolutamente certo se estiver lendo o código-fonte que respeita as convenções de estilo do C #. Nesse código:

this.count = 3;

atribui um valor a um campo.

count = 3;

atribui um valor a uma variável local.

Portanto, as convenções de estilo do C # são claras e inequívocas. Eles o forçam a não usar nenhum prefixo simplesmente porque você não precisa de um.

Quanto ao código-fonte para o qual os autores decidiram usar suas convenções pessoais, você deve perguntar -lhes pessoalmente por que eles fizeram uma escolha para fazer isso. Se eles não usarem prefixos, como sublinhados, embora não usem this., isso efetivamente levaria não apenas a códigos de leitura difícil, mas também a casos em que uma convenção adicional seria necessária:

public class Hello
{
    private string greetings;

    public Hello(string greetings)
    {
        greetings = greetings; // Wait, what is that?!
    }
}

C # não tem a palavra-chave this? Você não pode se referir a membros usando isso, como this.count?

Sim, mas (1) é mais digitado e (2) é fácil esquecer. Se o campo tiver o nome m_count, não preciso me lembrar de digitar this.m_count

Aqui, no entanto, você está errado.

É mais digitado quando você escreve seu código no Bloco de Notas. Com um IDE com preenchimento automático ou, melhor ainda, IntelliSense, a comparação entre this.counte m_countnão é tão óbvia. Note oShift + -necessário para digitar o sublinhado.

Quanto a “fácil de esquecer”, novamente, isso é relevante apenas para os usuários do Bloco de Notas. Não apenas o StyleCop e o Resharper não deixam você esquecer de colocar this.quando necessário, mas depois de algumas horas de programação, colocar this.quando necessário se torna um hábito.

Arseni Mourzenko
fonte
6
Acho que usar thissomente quando necessário resulta em uma sensação realmente inconsistente e me distrai com muita frequência. Se você usará em thisvez de um prefixo, acredito que você sempre deve usá-lo. Nesse caso, ele se torna um prefixo e você também pode usá-lo _.
precisa
1
RubberDuck está certo. "esta." é um prefixo, embora tenha significado de compilador.
precisa
4

O prefixo "member" é um tipo de detalhe de implementação. Isso distrai sua atenção do domínio do problema para o domínio da solução técnica, o que distorce sua mente ao pensar em possíveis refatorações. - mim

você pode explicar mais? A sua é a única razão que eu vi para não usar o prefixo e não o entendo ... (pelo que quero dizer, as outras coisas que as pessoas estão dizendo são razões pelas quais eu não preciso usá-lo, que não é a mesma coisa que eu não deveria) - Betty Crokker

Meu argumento é estender as respostas de @CandiedOrange e @Ewan.

Prefixos dificultam a refatoração

À medida que seu código evolui, variáveis ​​e métodos podem alterar seu escopo. Por exemplo. você pode criar um método privado e depois descobrir que ele também deve estar disponível para outras (novas) classes. Semelhante pode acontecer com os parâmetros do método e variáveis ​​locais.

Digamos que você crie uma calculadora de impostos. De acordo com o princípio do escopo de visibilidade da concessão para variáveis, você começa com um método que considera a taxa de imposto e o valor base como parâmetros:
(o exemplo pode parecer Java-ish ...)

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
} 

Em seguida, é necessário implementar a operação reversa e, de acordo com o TDD, você faz isso com o mínimo de esforço:

class TaxCalculator{
   double Calculate(double p_TaxRateInpercent, double p_BaseValue){
     return (100+p_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxRateInpercent, double p_TaxedValue){
     return p_TaxedValue/((100+p_TaxRateInpercent)/100);
   }
} 

O próximo TDD deseja que você refatore o código para se tornar mais limpo, o que reduz a lista de parâmetros dos dois métodos.
(Sim, você extrairia primeiro o cálculo dobrado, mas deixe-me entender ...)
A solução óbvia é alterar a taxa de imposto em uma variável membro passando-a como um parâmetro construtor.

class TaxCalculator{
   double m_TaxRateInpercent;
   TaxCalculator(double p_TaxRateInpercent){
     m_TaxRateInpercent = p_TaxRateInpercent;
   }
   double Calculate(double p_BaseValue){
     return (100+m_TaxRateInpercent)/100 * p_BaseValue;
   }
   double CalculateBase(double p_TaxedValue){
     return p_TaxedValue/((100+m_TaxRateInpercent)/100);
   }
} 

Como você pode ver, tivemos que alterar todas as linhas de nossos métodos existentes (qualquer linha que usamosp_TaxRateInpercent ser exata.

O problema é que você não tem suporte do seu IDE para renomear em toda a classe. A única ajuda para pesquisar / substituir também mudará o construtor ou qualquer parte que contenha acidentalmente a string p_TaxRateInpercent.
Seu IDE pode oferecer uma alteração em ... refatoração para nomes de variáveis ​​indefinidos, mas isso pode ser restrito ao escopo do método

Sem o prefixo, apenas as assinaturas do método teriam sido alteradas. Nenhuma renomeação seria necessária.


Prefixos desorganizam o histórico do SCM quando alterado

Além disso, o SCM registra a alteração do prefixo como alterações na lógica, embora a lógica não tenha sido alterada! O histórico de SCMs está confuso com essa mudança técnica, ocultando o que é importante e aumentando o risco de conflitos com as mudanças (lógicas reais) de outras pessoas.

Timothy Truckle
fonte
1

Eu uso o prefixo _ para todas as variáveis ​​de membro. Odeio o prefixo 'this' porque, embora alguns afirmem que é a maneira certa de fazer as coisas - ele adiciona muito ruído / confusão à sua base de código. Um único sublinhado também é uma prática comum nos exemplos da Microsoft.

Então, no seu exemplo, eu teria:

int _count = 10;
Eternal21
fonte