C # - Pourquoi les préfixes des champs sont-ils découragés?

19

Dans le passé, nous avons fait de la notation hongroise. C'est maintenant considéré comme dépassé , et pour la plupart je ne l'utilise plus, mais je trouve toujours utilisé le m_préfixe pour indiquer les champs membres.

Pour moi, si je lis le code de quelqu'un d'autre, et je vois ceci:

count = 3;

Je suppose que countc'est une variable locale à cette fonction et je fais quelque chose qui sera utilisé ailleurs dans la fonction; si je vois ça:

m_count = 3;

Je réalise immédiatement que je mets à jour l'état de l'objet.

Les directives de style Microsoft disent que c'est la mauvaise façon de faire les choses. Je suis censé nommer mes champs non publics exactement comme des variables temporaires définies dans la fonction. Non m_, pas même un simple soulignement.

Oui, nous pouvons définir notre propre style de codage, mais ensuite je dois lutter avec divers outils d'analyse de code statique, pour les convaincre que notre façon de faire est OK.

Je suis heureux de passer au style Microsoft, mais j'aimerais savoir pourquoi les choses sont comme elles sont.

Pourquoi est-il maintenant considéré comme mauvais de pouvoir dire si une variable est une fonction locale ou un membre?

PS Ceci est très similaire à Quelles sont les meilleures pratiques actuellement considérées concernant le mot-clé «this» devant le champ et les méthodes en c #? , mais je ne demande m_pasthis.

PPS Voir aussi Pourquoi Microsoft a-t-il fait en sorte que les paramètres, les variables locales et les champs privés aient la même convention de dénomination de nom?

Betty Crokker
la source
9
C # n'a-t-il pas le thismot-clé? Ne pourriez-vous pas vous référer aux membres utilisant this, comme this.count?
FrustratedWithFormsDesigner
3
Le préfixe m_ est un isme C ++ où il évite les conflits de noms entre les champs et les méthodes. En C #, ce n'est pas un problème car les noms de méthode doivent commencer par des majuscules, les champs par des minuscules.
amon
4
Si vous imprimez du code en 2017, vous faites quelque chose de mal. Dans les deux autres scénarios, il devrait être assez évident de countne pas apparaître de nulle part car il n'a pas été déclaré dans la méthode. Franchement, l'argument "out of IDE" est vraiment faible. D'autant plus qu'il existe même des outils de révision de code qui fonctionnent dans l'IDE.
Andy
4
Message toujours pertinent de Joel .
Kasey Speakman

Réponses:

19

Quand ils travaillaient sur l'API pour Windows, Microsoft a recommandé l'utilisation de la notation hongroise, en utilisant «m_», ainsi que «i», «sz», etc. À l'époque, cela était utile car l'IDE n'était pas robuste assez pour vous montrer cette information.

C #, d'autre part, a un IDE très robuste dès le départ.

Ils ont donc fait la recommandation dans les lignes directrices de nommage de Microsoft pour C # pour les champs publics statiques ou protégés:

 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.

Maintenant que vous pouvez survoler avec votre souris ou appuyer sur CTRL + K + W et obtenir toutes les informations sur le membre, Microsoft a déterminé que les préfixes sont de mauvaise forme; ils sont une solution manuelle à un problème déjà résolu.

Les champs privés sont spécifiquement exclus des lignes directrices, mais Microsoft semble être parvenu à la conclusion que c'est le cas même pour les champs privés qui progressent en interne, ce que vous pouvez voir si vous pointez Resharper sur .NET 4.5 ou .NET Core.

Utilisez vos outils, ne le faites pas manuellement.

Kevin Fee
la source
2
Même réponse que pour Andy ... oui mais ... il y a de nombreuses situations où je regarde du code en dehors de l'IDE. Exemples - (1) outils de fusion. (2) outils de révision de code. (3) impressions (halètement).
Betty Crokker
Andy a posté littéralement le même instant que moi. J'ai vu apparaître "1 nouveau commentaire" lorsque j'ai cliqué sur "ajouter une réponse". En ce qui concerne ces outils, deux d'entre eux sont déjà dans l'IDE. Imprimés ... pourquoi faites-vous cela?
Kevin Fee
1
Ma question initiale reste sans réponse ... oui, Microsoft a dit de ne pas utiliser m_ et les fabricants d'outils ont suivi cela, mais ce n'est pas une raison technique d'utiliser m_, c'est ce qu'on appelle souvent "faire appel à l'autorité". La seule raison technique que j'ai vue pour ne pas utiliser m_ est venue de Timothy Truckle et je ne le comprends pas ...
Betty Crokker
8
@BettyCrokker Parce que m_ n'ajoute absolument aucune valeur. L'IDE vous dit membre ou non, donc le m_ est redondant. Pourquoi êtes-vous obsédé par m_? Pourquoi pas l_ (pour local)? Pourquoi pas afsdfjdskfjas_? Je vais dévaloriser la question, car c'est un argument religieux stupide de toute façon. Fais ce que tu veux. Les directives que vous frappez disent également "Les directives de dénomination des champs s'appliquent aux champs publics et protégés statiques . Les champs internes et privés ne sont pas couverts par les directives , et les champs d'instance publics ou protégés ne sont pas autorisés par les directives de conception des membres."
Andy
1
@BettyCrokker OK, mais vous posez des questions sur les directives qui sont faites avec cette hypothèse. Vous vous plaignez également des outils statiques qui utilisent probablement cette hypothèse également, car "personne" (dans une marge d'erreur statistique acceptable) n'imprime son code C #. Je ne sais pas quels outils d'analyse statistique vous utilisez, mais Resharper est assez spécifique: les champs privés sont "_lowerCase". Les membres publics de tout type ou "UpperCase", et les locaux sont "lowerCase". Économiser sur le "m" semble en fait assez agréable, de toute façon.
Kevin Fee
17

C # n'a pas de variables globales, ce qui ne laisse que des variables locales et des champs de classe.

Et si vous avez tellement de variables locales que vous ne savez plus si un identifiant en est un, vous avez très certainement violé l'une des directives pour gérer la complexité le plus gravement.

Essayez d'extraire un objet-paramètre, essayez de diviser votre fonction en plusieurs plus simples, vous devez très certainement refactoriser votre code, sauf si vous aimez vous noyer dans les minuties et la complexité, ainsi que ne pas tenir compte de la maintenabilité.

Maintenant que nous avons établi que le but des décorations "ceci est un membre" ne tient plus car la portée de la fonction doit être trop petite et la portée globale n'existe pas, il y a un inconvénient à ces marqueurs: ils inhibent déplacer des arguments / variables locales vers les membres, ou l'inverse.

Déduplicateur
la source
Une fois que vous l'avez pointé, il peut également s'agir d'une classe ou d'un espace de noms.
CodesInChaos
3
Je ne pense pas que cela réponde vraiment à la question.
Vladimir Stokic
5
@FrankHileman En fait, ça l'est. Cela explique pourquoi il n'y a pas de bonne raison d'uglifier un nom.
Déduplicateur
2
"uglifiant"? Si tel est le cas, il s'agit uniquement d'une question d'opinion. Tout le monde peut avoir une opinion différente sur la laideur. Il y a des raisons de préférer une convention à une autre, mais il n'y a pas de convention standard dans ce cas.
Frank Hileman
1
La beauté de @Deduplicator est dans l'œil du spectateur. Des conventions que je considérais jadis comme laides, je les apprécie maintenant comme utiles.
Frank Hileman
11

Pourquoi les développeurs évitent-ils d'ajouter des espaces de noms avec la directive using namespace?

System.DateTime()est beaucoup plus explicite que DateTime(). Cela me dit exactement à quoi je fais face. Est-ce simplement parce que nous sommes des dactylographes paresseux?

Non, nous sommes des dactylographes paresseux, mais ce n'est pas pourquoi.

Nous préférons les styles de codage qui nous permettent d'ignorer les détails et de nous concentrer sur les abstractions. Forcer les noms à communiquer les détails de la structure est distrayant. Quand je travaille sur un algorithme compliqué, je ne veux pas de noms qui insistent pour me rappeler chaque petit détail de la chose avec laquelle je travaille. J'ai juste besoin du nom pour m'empêcher de confondre Timeret DateTime. Je vais m'inquiéter de savoir si ceux-ci sont compatibles ailleurs.

C'est la même raison pour laquelle vous ne voulez pas que deux gars de votre équipe s'appellent Jon. Le fait qu'ils aient des noms de famille n'est pas une raison pour leur donner des préfixes ou des suffixes. Je vais juste t'appeler Skeet. Rien de plus est une douleur à plat.

candied_orange
la source
2
Cela ne semble pas répondre à la question. Il n'y a aucune préférence ou convention générale pour les champs privés dans .net.
Frank Hileman
12
@FrankHileman En général, nous préférons les bons noms. Les conventions ne créent pas de bons noms. Les conventions créent des noms comme McOhPlease Von StopItAlreadyStine.
candied_orange
Les conventions visent simplement à faciliter le travail avec différents codes de développeur. Votre réponse ne concerne hongrois, mais il n'y a pas de convention pour les champs privés, donc la question est basée sur une fausse hypothèse.
Frank Hileman
7

Développons un peu.

Il existe 3 styles de préfixe communs, qui se rencontrent tous occasionnellement dans le code c #:

  • m_
  • _
  • cette.

Ces préfixes sont couramment utilisés dans l'implémentation privée d'une classe . La recommandation Microsoft concerne principalement les parties exposées d'une classe.

L'avantage d'utiliser m_over _est que le préfixe peut être le même dans toute votre base de code si vous utilisez c # ainsi que dans les langues où l'utilisation en _tant que préfixe n'est pas autorisée . * L'avantage de m_over this.est qu'il est plus court et que vous ne le faites pas accidentellement oubliez le préfixe.

L'avantage de _over m_est qu'il est plus court et que tout ce qui commence par le préfixe est trié en haut est trié alphabétiquement par un outil. L'avantage de _over this.est qu'il est plus court et que vous n'oublierez pas accidentellement le préfixe.

L'avantage de this.over m_et _c'est que vous pouvez l'utiliser dans une base de code où _ou m_ne sont pas une convention acceptée et universelle. Cela signifie également que personne n'a besoin de connaître la convention _ou m_, ce qui peut être considéré comme simplifiant les choses. Comme inconvénient, parce que le compilateur ne se soucie pas de ce préfixe, le this.préfixe sera parfois manquant et, en tant que tel, ne sera pas aussi fiable qu'un indicateur.


* Une fois que vous commencez à accéder aux classes C # qui utilisent à _partir de C ++ / CLI (qui ne devraient vraiment pas vraiment utiliser _), vous remarquerez que s'accorder sur un préfixe commun est plus complexe qu'il ne devrait l'être. Décider de ne pas avoir de préfixe supprime ce bruit. Combinez cela avec la réponse de Deduplicator, que je vais paraphraser comme "l'avantage d'un préfixe est presque négligeable", et cela nous donne: "Il y a un petit problème lors de l'utilisation des préfixes, et un petit avantage, donc c'est un choix valide de ne pas simplement Utilise les".


Il y a une question plus ancienne sur stackoverflow liée à cela.

Peter - Unban Robert Harvey
la source
1
Belle comparaison. Cependant, je trouve que ne pas utiliser de préfixe fonctionne bien dans la pratique.
Phil1970
6

Il convient de noter que le guide de style MS ne parle que des méthodes et des variables publiques et protégées. c'est-à-dire ceux que les autres développeurs verront s'ils utilisent votre bibliothèque.

L'idée étant que les bibliothèques Microsoft reçoivent un aspect standard pour les développeurs qui les consomment.

Vous êtes libre d'utiliser le style de votre choix sur vos variables privées ou locales.

Cela dit, les préfixes variables sont généralement découragés pour un certain nombre de raisons

  • Ils peuvent être faux et donc trompeurs
  • Si vous préfixez par type de variable, la façon dont vous gérez les classes que vous avez créées n'est pas claire.
  • Il existe un certain nombre de conventions et elles ne sont pas toujours d'accord. Ce que vous trouvez utile qu'un autre développeur pourrait trouver inutile
  • Dans le cas des variables membres, vous pouvez utiliser ce «ceci». mot-clé pour indiquer la portée et le complice l'appliquera.
Ewan
la source
2
À l'origine, je n'utilisais aucun préfixe pour les champs. Plus tard, je suis passé à l'utilisation d'un seul caractère "_", car j'en ai vu d'autres l'utiliser, car il déplace tous les champs en haut de la liste des membres alphabétisés lorsque vous utilisez les zones de liste déroulante IDE. L'absence d'une convention standard peut être gênante lors de la lecture de code écrit par de nombreux développeurs différents.
Frank Hileman
après un certain temps, vous voyez tellement de styles différents que vous les ignorez. Si vous essayez d'appliquer un style, vous refactorisez constamment tout
Ewan
4
Si vous appliquez «Ils peuvent être faux, et donc trompeurs» à presque tout, vous découvrez rapidement que vous ne devez pas nommer de variables ou de fonctions - ils peuvent être faux après tout! Et je suppose que votre deuxième puce est censée dire "des classes que vous n'avez PAS créées"? Ou bien je ne comprends tout simplement pas ... Dans tous les cas, cela commence à ressembler à avoir un seul préfixe de soulignement pour les champs non publics est une norme non officielle, c'est probablement la direction que nous irons.
Betty Crokker
certaines choses sont vérifiées par le compilateur, donc m_count n'est peut-être pas une variable membre, mais this.count le sera toujours
Ewan
beaucoup de ppl utilisent _ mais les choses changent avec le temps, vous constaterez que le code écrit dans les «meilleures pratiques» l'année dernière ne correspond pas aux conventions de cette année
Ewan
4

Pour moi, si je lis le code de quelqu'un d'autre, et je vois ceci:

count = 3;

Je suppose que 'count' est une variable locale à cette fonction et je fais quelque chose qui sera utilisé ailleurs dans la fonction

Et vous aurez tout à fait raison si vous lisez du code source qui respecte les conventions de style de C #. Dans un tel code:

this.count = 3;

attribue une valeur à un champ.

count = 3;

assigne une valeur à une variable locale.

Par conséquent, les conventions de style de C # sont claires et sans ambiguïté. Ils vous obligent à ne pas utiliser de préfixe simplement parce que vous n'en avez pas besoin.

Quant au code source pour lequel les auteurs ont décidé d'utiliser leurs conventions personnelles à la place, vous devriez demander leur personnellement pourquoi ils ont choisi de le faire. S'ils n'utilisent pas de préfixes tels que des traits de soulignement, sans les utiliser non this.plus, cela entraînerait effectivement non seulement un code difficile à lire, mais aussi des cas où une convention supplémentaire serait nécessaire:

public class Hello
{
    private string greetings;

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

C # n'a-t-il pas le mot clé this? Ne pourriez-vous pas vous référer aux membres utilisant ceci, comme this.count?

Oui, mais (1) c'est plus de frappe et (2) c'est facile à oublier. Si le champ est nommé m_count, je n'ai pas besoin de me rappeler de taper this.m_count

Ici, cependant, vous vous trompez.

C'est plus de frappe lorsque vous écrivez votre code dans le Bloc-notes. Avec un IDE avec auto-complétion ou, mieux, IntelliSense, la comparaison entre this.countet m_countn'est pas aussi évidente. Noter laShift + -requis pour taper le trait de soulignement.

Quant à «facile à oublier», encore une fois, cela ne concerne que les utilisateurs du Bloc-notes. Non seulement StyleCop et Resharper ne vous laisseront pas oublier de mettre en this.cas de besoin, mais après quelques heures de programmation, mettre en this.cas de besoin devient une habitude.

Arseni Mourzenko
la source
6
Je trouve que l'utilisation thisuniquement lorsque vous en avez besoin entraîne une sensation vraiment incohérente et me distrait beaucoup trop souvent. Si vous allez utiliser thisau lieu d'un préfixe, je pense que vous devriez toujours l' utiliser. Dans ce cas, il devient un préfixe et vous pourriez aussi bien l'utiliser _.
RubberDuck
1
RubberDuck a raison. "cette." est un préfixe, bien qu'il ait une signification de compilateur.
Frank Hileman
4

Le préfixe "membre" est une sorte de détail d'implémentation. Il détourne votre attention du domaine du problème vers le domaine de la solution technique qui biaise votre esprit lorsque vous pensez à des refactorings possibles. - moi

pouvez-vous expliquer davantage? C'est la seule raison pour laquelle j'ai vu pourquoi ne pas utiliser le préfixe et je ne le comprends pas ... (par ce que je veux dire, les autres choses que les gens disent sont des raisons pour lesquelles je n'ai pas à l'utiliser, ce qui n'est pas la même chose que pourquoi je ne devrais pas) - Betty Crokker

Mon point est d'étendre les réponses de @CandiedOrange et @Ewan.

Les préfixes rendent le refactoring plus difficile

À mesure que votre code évolue, les variables et les méthodes peuvent changer leur portée. Par exemple. vous pouvez créer une méthode privée et découvrir plus tard qu'elle devrait également être disponible pour d'autres (nouvelles) classes. Des paramètres similaires et des variables locales peuvent se produire.

Supposons que vous créez un calculateur de taxes. Selon le principe de l' étendue de visibilité du bail pour les variables, vous commencez avec une méthode qui prend à la fois le taux de taxe et la valeur de base comme paramètres:
(l'exemple peut ressembler à Java-ish ...)

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

ensuite vous devez implémenter l'opération inverse et selon TDD vous le faites avec le moins d'effort:

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);
   }
} 

TDD veut que vous refactorisez le code pour le rendre plus propre, ce qui revient à réduire la liste des paramètres des deux méthodes.
(Oui, vous extrairez d'abord le calcul doublé, mais permettez-moi de comprendre mon point ...)
La solution évidente est de changer le taux d'imposition en une variable membre en le passant comme paramètre constructeur.

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);
   }
} 

Comme vous pouvez le voir, nous avons dû changer toutes les lignes de nos méthodes existantes (toute ligne que nous avions l'habitude p_TaxRateInpercentd'être exacte.

Le problème est que vous n'avez aucun support de votre IDE pour faire le changement de nom dans toute la classe. La seule aide à rechercher / remplacer qui changera également le constructeur ou toute partie qui contient accidentellement la chaîne p_TaxRateInpercent.
Vous IDE pouvez proposer une modification de ... refactoring pour les noms de variables non définis, mais cela peut être limité à la portée de la méthode

Sans le préfixe, seules les signatures de méthode auraient changé. Aucun changement de nom n'aurait été nécessaire.


Les préfixes encombrent l'historique SCM lorsqu'ils sont modifiés

Le SCM enregistre également le changement de préfixe comme des changements dans la logique bien que la logique n'ait pas changé! L'histoire des SCM est encombrée de ce changement technique qui cache ce qui est important et augmente le risque de conflits avec les changements (réels) des autres.

Timothy Truckle
la source
1

J'utilise le préfixe _ pour toutes les variables membres. Je déteste «ce» préfixe car, alors que certains prétendent que c'est la bonne façon de faire les choses - cela ajoute beaucoup de bruit / encombrement à votre base de code. Un seul trait de soulignement est également une pratique courante dans les exemples de Microsoft.

Donc, dans votre exemple, j'aurais:

int _count = 10;
Eternal21
la source