web-dev-qa-db-fra.com

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

Dans le passé, nous avons fait de la notation hongroise. C'est maintenant considéré comme passé , et pour la plupart je ne l'utilise plus, mais je trouve toujours une utilisation pour 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 count 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 trait de soulignement.

Oui, nous pouvons définir notre propre style de codage, mais je dois ensuite 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?

P.S. 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 pose des questions sur m_ ne pas this.

P.P.S. 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?

21
Betty Crokker

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 le IDE n'était pas assez robuste pour vous montrer ces informations.

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

Ils ont donc fait la recommandation dans Microsoft's Naming Guidelines for 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.

20
Kevin Fee

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 pas si un identifiant en est un, vous avez 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.

17
Deduplicator

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?

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 Timer et 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.

12
candied_orange

Développons un peu.

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

  • m_
  • _
  • cette.

De tels 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_ plus de _ est que le préfixe peut être le même dans l'ensemble de votre base de code si vous utilisez c # ainsi que langues où utiliser _en tant que préfixe n'est pas autorisé . * L'avantage de m_ plus de this. est qu'il est plus court et que vous n'oublierez pas accidentellement le préfixe.

L'avantage de _ plus de 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 _ plus de this. est qu'il est plus court et que vous n'oublierez pas accidentellement le préfixe.

L'avantage de this. plus de m_ et _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 le _ ou m_ convention, qui peut être considérée comme simplifiant les choses. Comme inconvénient, parce que le compilateur ne se soucie pas de ce préfixe, le this. le préfixe sera parfois manquant, et en tant que tel, il ne sera pas aussi fiable qu'un indicateur.


* Une fois que vous commencez à accéder aux classes C # qui utilisent _ de C++/CLI (qui ne devrait vraiment pas utiliser _), vous remarquerez que se mettre d'accord 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 valide choix de ne pas les utiliser ".


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

7
Peter

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.
6
Ewan

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 ne dois 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 périmètre de visibilité du bail pour les variables, vous commencez avec une méthode qui prend les deux, le taux de taxe et le 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 qu'il devienne plus propre, ce qui réduit 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 taux de taxe en une variable membre en la 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û modifier toutes les lignes de nos méthodes existantes (toute ligne que nous avons utilisée p_TaxRateInpercent pour être exact.

Le problème est que vous avez pas de support de votre IDE pour faire le changement de nom dans toute la classe. La seule aide recherche/remplacement qui changera également le constructeur ou toute partie qui contient accidentellement la chaîne p_TaxRateInpercent.
Vous IDE pourrait proposer un changer en ... 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

De plus, le SCM enregistre le changement de préfixe en tant que 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.

5
Timothy Truckle

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 absolument 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 leur demander personnellement pourquoi ils ont choisi de le faire. S'ils n'utilisent pas de préfixes tels que des traits de soulignement, alors qu'ils n'utilisent pas this. non plus, cela entraînerait 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.count et m_count n'est pas aussi évident. Noter la Shift+- nécessaire 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 this. au besoin, mais après quelques heures de programmation, mettre this. en cas de besoin devient une habitude.

4
Arseni Mourzenko

J'utilise le préfixe _ pour toutes les variables membres. Je déteste le préfixe "this" car, alors que certains prétendent que c'est la bonne façon de faire les choses - cela ajoute beaucoup de bruit/d'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;
1
Eternal21