Warum werden Präfixe in Feldern nicht empfohlen?

19

Früher haben wir ungarische Noten geschrieben. Das ist jetzt passé und ich benutze es größtenteils nicht mehr, aber ich finde immer noch Verwendung für das m_Präfix, um Mitgliedsfelder anzugeben.

Für mich, wenn ich den Code einer anderen Person durchlese und Folgendes sehe:

count = 3;

Ich countgehe davon aus, dass dies eine lokale Variable für diese Funktion ist, und mache etwas, das an anderer Stelle in der Funktion verwendet wird. wenn ich das sehe:

m_count = 3;

Mir wird sofort klar, dass ich den Status des Objekts aktualisiere.

Nach den Microsoft-Richtlinien ist dies die falsche Vorgehensweise. Ich soll meine nicht öffentlichen Felder genauso benennen wie temporäre Variablen, die in der Funktion definiert sind. Nein m_, nicht einmal ein einfacher Unterstrich.

Ja, wir können unseren eigenen Codierungsstil definieren, aber dann muss ich mich mit verschiedenen statischen Codeanalyse-Tools herumschlagen, um sie davon zu überzeugen, dass unsere Vorgehensweise in Ordnung ist.

Ich bin glücklich, zum Microsoft-Stil zu wechseln, aber ich würde gerne wissen, warum die Dinge so sind, wie sie sind.

Warum wird es jetzt als schlecht angesehen, feststellen zu können, ob eine Variable eine lokale Funktion oder ein Member ist?

PS Dies ist sehr ähnlich zu Was ist die angesehenen aktuellen Best Practices in Bezug auf das Schlüsselwort "this" vor Feld und Methoden in c #? , aber ich frage nach m_nichtthis.

PPS Siehe auch Warum hat Microsoft Parameter, lokale Variablen und private Felder mit der gleichen Namenskonvention versehen?

Betty Crokker
quelle
9
Hat C # das thisSchlüsselwort nicht? Könnten Sie nicht auf Mitglieder verweisen, die verwenden this, wie this.count?
FrustratedWithFormsDesigner
3
Das Präfix m_ ist ein C ++ - Ismus, mit dem Namenskonflikte zwischen Feldern und Methoden vermieden werden. In C # ist dies kein Problem, da Methodennamen mit Großbuchstaben beginnen sollten, Felder mit Kleinbuchstaben.
Amon
4
Wenn Sie 2017 Code drucken, machen Sie etwas falsch. In den anderen beiden Szenarien sollte es ziemlich offensichtlich sein, dass countes nicht nur aus dem Nichts heraus erschien, da es in der Methode nicht deklariert wurde. Ehrlich gesagt ist das Argument "out of IDE" WIRKLICH schwach. Zumal es sogar Code-Review-Tools gibt, die in der IDE funktionieren.
Andy
4
Noch relevanter Beitrag von Joel .
Kasey Speakman

Antworten:

19

Als sie an der API für Windows arbeiteten, empfahl Microsoft die Verwendung der ungarischen Notation mit 'm_' sowie 'i', 'sz' usw. Zu dieser Zeit war dies nützlich, da die IDE nicht robust war genug, um Ihnen diese Informationen zu zeigen.

C # hingegen hat von Anfang an eine sehr robuste IDE.

Daher haben sie in den Microsoft-Namensrichtlinien für C # die folgenden Empfehlungen für öffentliche statische oder geschützte Felder gegeben:

 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.

Nachdem Sie mit der Maus darüber fahren oder STRG + K + W drücken und alle Informationen über das Mitglied abrufen können, hat Microsoft festgestellt, dass die Präfixe eine schlechte Form haben. Sie sind eine manuelle Lösung für ein bereits gelöstes Problem.

Private Felder sind ausdrücklich von den Richtlinien ausgenommen. Microsoft scheint jedoch zu dem Schluss gekommen zu sein, dass dies auch für private Felder gilt, die intern weiterentwickelt werden. Dies lässt sich feststellen, wenn Sie Resharper auf .NET 4.5 oder .NET Core verweisen.

Verwenden Sie Ihre Werkzeuge, nicht manuell.

Kevin Fee
quelle
2
Dieselbe Antwort wie für Andy ... ja, aber ... es gibt viele Situationen, in denen ich Code außerhalb der IDE betrachte. Beispiele - (1) Werkzeuge zusammenführen. (2) Tools zur Codeüberprüfung. (3) (Keuchen) Ausdrucke.
Betty Crokker
Andy hat buchstäblich in dem Moment gepostet, als ich es getan habe. Ich sah den "1 neuen Kommentar" als ich auf "Antwort hinzufügen" klickte. In Bezug auf diese Tools befinden sich zwei davon bereits in der IDE. Ausdrucke ... warum machst du das?
Kevin Fee
1
Meine anfängliche Frage bleibt unbeantwortet ... Ja, Microsoft hat gesagt, dass m_ nicht verwendet wird, und die Werkzeughersteller sind dem gefolgt, aber das ist kein technischer Grund, m_ zu verwenden, es wird oft als "Aufruf an die Behörde" bezeichnet. Der einzige technische Grund, warum ich gesehen habe, m_ nicht zu verwenden, kam von Timothy Truckle und ich verstehe es nicht ...
Betty Crokker
8
@BettyCrokker Weil m_ absolut keinen Wert hinzufügt. Die IDE sagt Ihnen, Mitglied oder nicht, also ist das m_ redundant. Warum bist du auf m_ fixiert? Warum nicht l_ (für Einheimische)? Warum nicht afsdfjdskfjas_? Ich werde die Frage ablehnen, weil es sowieso ein albernes religiöses Argument ist. Mach was du willst. In den Richtlinien, die Sie anklopfen, heißt es auch: "Die Richtlinien für die Benennung von Feldern gelten für statische öffentliche und geschützte Felder . Interne und private Felder werden von Richtlinien nicht abgedeckt , und öffentliche oder geschützte Instanzfelder sind von den Richtlinien für den Entwurf von Mitgliedern nicht zulässig."
Andy
1
@BettyCrokker OK, aber Sie fragen nach Richtlinien, die mit dieser Annahme erstellt wurden. Sie beschweren sich auch über statische Tools, die diese Annahme wahrscheinlich ebenfalls verwenden, da "niemand" (innerhalb einer akzeptablen statistischen Fehlergrenze) seinen C # -Code ausgibt. Ich weiß nicht, welche statistischen Analysetools Sie verwenden, aber Resharper ist ziemlich spezifisch: Private Felder sind "_lowerCase". Öffentliche Mitglieder jeglichen Typs oder "UpperCase" sowie Einheimische sind "lowerCase". Das Speichern auf dem "m" sieht eigentlich sowieso ganz nett aus.
Kevin Fee
17

C # hat keine globalen Variablen, so dass nur lokale Variablen und Klassenfelder übrig bleiben.

Und wenn Sie so viele lokale Variablen haben, dass Sie nicht mehr wissen, ob es sich bei einem Bezeichner um einen handelt, haben Sie mit größter Sicherheit gegen eine der Richtlinien zum Umgang mit Komplexität verstoßen.

Versuchen Sie, ein Parameter-Objekt zu extrahieren, versuchen Sie, Ihre Funktion in mehrere einfachere zu unterteilen. Sie sollten mit Sicherheit Ihren Code umgestalten, es sei denn, Sie möchten in Details und Komplexität ertrinken und haben keine Rücksicht auf die Wartbarkeit.

Nachdem wir festgestellt haben, dass der Zweck von "this-is-a-member" -Dekorationen nicht mehr besteht, da der Funktionsumfang zu klein sein sollte und der globale Geltungsbereich nicht existiert, gibt es einen Nachteil dieser Marker: Sie hemmen Verschieben von Argumenten / lokalen Variablen zu Mitgliedern oder umgekehrt.

Deduplizierer
quelle
Sobald Sie sich damit befasst haben, kann es sich auch um eine Klasse oder einen Namespace handeln.
CodesInChaos
3
Ich denke, das beantwortet die Frage nicht wirklich.
Vladimir Stokic
5
@FrankHileman Eigentlich ist es. Es erklärt, warum es keinen guten Grund gibt, einen Namen zu hässlich zu machen.
Deduplizierer
2
"hässlich"? In diesem Fall handelt es sich um eine reine Meinungsfrage. Jeder kann eine andere Meinung über Hässlichkeit haben. Es gibt Gründe, eine Konvention einer anderen vorzuziehen, aber in diesem Fall gibt es keine Standardkonvention.
Frank Hileman
1
@Deduplicator Schönheit liegt im Auge des Betrachters. Konventionen, die ich einmal als hässlich empfunden habe, schätze ich jetzt als nützlich ein.
Frank Hileman
11

Warum vermeiden Entwickler das Voranstellen von Namespaces mit der using-Namespace-Direktive?

System.DateTime() ist viel expliziter als DateTime() . Es sagt mir genau, womit ich es zu tun habe. Liegt es nur daran, dass wir faule Schreibkräfte sind?

Nein, wir sind faule Schreibkräfte, aber das ist nicht der Grund.

Wir bevorzugen Codierungsstile, mit denen wir Details ignorieren und uns auf Abstraktionen konzentrieren können. Das Erzwingen von Namen zur Übermittlung von Strukturdetails lenkt ab. Wenn ich an einem komplizierten Algorithmus arbeite, möchte ich keine Namen, die darauf bestehen, mich an jedes Detail der Sache zu erinnern, mit der ich arbeite. Ich brauche nur den Namen, um mich nicht zu verwirren Timerund DateTime. Ich mache mir Sorgen, ob diese irgendwo anders kompatibel sind.

Es ist der gleiche Grund, warum du nicht zwei Jungs in deinem Team namens Jon haben willst. Die Tatsache, dass sie Nachnamen haben, ist kein Grund, ihnen Präfixe oder Suffixe zu geben. Ich werde dich nur Skeet nennen. Alles andere ist ein totaler Schmerz.

kandierte_orange
quelle
2
Dies scheint die Frage nicht zu beantworten. Es gibt keine allgemeinen Präferenzen oder Konventionen für private Felder in .net.
Frank Hileman
12
@FrankHileman Im Allgemeinen bevorzugen wir gute Namen. Konventionen schaffen keine guten Namen. Konventionen erstellen Namen wie McOhPlease Von StopItAlreadyStine.
candied_orange
Konventionen dienen lediglich dazu, die Arbeit mit dem Code anderer Entwickler zu erleichtern. Ihre Antwort bezieht sich zwar auf Ungarisch, es gibt jedoch keine Konvention für private Felder, sodass die Frage auf einer falschen Annahme basiert.
Frank Hileman
7

Lassen Sie uns ein bisschen erweitern.

Es gibt drei gängige Präfixstile, die gelegentlich im c # -Code vorkommen:

  • m_
  • _
  • Dies.

Solche Präfixe werden üblicherweise bei der privaten Implementierung einer Klasse verwendet . Die Microsoft-Empfehlung bezieht sich hauptsächlich auf die exponierten Teile einer Klasse.

Der Vorteil der Verwendung von m_over _ist, dass das Präfix in Ihrer gesamten Codebasis identisch sein kann, wenn Sie c # sowie Sprachen verwenden, in denen die Verwendung _als Präfix nicht zulässig ist . * Der Vorteil von m_over this.ist, dass es kürzer ist und Sie nicht versehentlich vergiss das Präfix.

Der Vorteil von _over m_ist, dass es kürzer ist und dass alles, was oben mit dem Präfix beginnt, von einem Tool alphabetisch sortiert wird. Der Vorteil von _over this.ist, dass es kürzer ist und Sie das Präfix nicht versehentlich vergessen.

Der Vorteil von this.over m_and _ist, dass Sie es in einer Codebasis verwenden können, in der es sich um eine akzeptierte und universelle Konvention handelt _oder m_nicht. Das bedeutet auch, dass niemand etwas über die _oder m_Konvention wissen muss , was als Vereinfachung angesehen werden kann. Als Nachteil, da der Compiler sich nicht um dieses Präfix kümmert, wird das this.Präfix gelegentlich fehlen und als solches nicht so zuverlässig wie ein Indikator sein.


* Sobald Sie mit dem Zugriff auf C # -Klassen beginnen, die _über C ++ / CLI verwendet werden (was eigentlich eigentlich nicht verwendet werden sollte _), werden Sie feststellen, dass die Vereinbarung eines gemeinsamen Präfix komplexer ist, als es sein sollte. Die Entscheidung, kein Präfix zu haben, beseitigt dieses Rauschen. Kombinieren Sie dies mit der Antwort von Deduplicator, die ich als "der Vorteil eines Präfixes ist fast vernachlässigbar" umschreibe und die uns ergibt: "Es gibt ein winziges Problem bei der Verwendung von Präfixen und einen winzigen Vorteil, daher ist es eine gültige Wahl, dies einfach nicht zu tun benutze sie".


Es gibt eine ältere Frage zum Stackoverflow .

Peter - Unban Robert Harvey
quelle
1
Netter Vergleich. Ich finde jedoch, dass es in der Praxis gut funktioniert, kein Präfix zu verwenden.
Phil1970
6

Es ist zu beachten, dass der MS-Styleguide nur öffentliche und geschützte Methoden und Variablen behandelt. Das heißt, die anderen Entwickler werden sehen, ob sie Ihre Bibliothek benutzen.

Die Idee ist, dass Microsoft-Bibliotheken Entwicklern, die sie nutzen, ein einheitliches Erscheinungsbild geben.

Sie können einen beliebigen Stil für Ihre privaten oder lokalen Variablen verwenden.

Allerdings wird von variablen Präfixen aus verschiedenen Gründen generell abgeraten

  • Sie können falsch und daher irreführend sein
  • Wenn Sie Variablen voranstellen, ist nicht klar, wie Sie mit von Ihnen erstellten Klassen umgehen.
  • Es gibt eine Reihe von Konventionen und sie stimmen nicht immer überein. Was Sie für hilfreich halten, könnte ein anderer Entwickler für wenig hilfreich halten
  • Im Fall von Mitgliedsvariablen können Sie dieses 'this' verwenden. Schlüsselwort, um den Bereich anzugeben, und der Complier wird es erzwingen.
Ewan
quelle
2
Ursprünglich habe ich für Felder überhaupt kein Präfix verwendet. Später habe ich auf ein einzelnes "_" umgestellt, da es bei Verwendung von IDE-Dropdown-Listenfeldern alle Felder an den oberen Rand der alphabetisch sortierten Mitgliederliste verschiebt. Das Fehlen einer Standardkonvention kann beim Lesen von Code, der von vielen verschiedenen Entwicklern geschrieben wurde, ärgerlich sein.
Frank Hileman
Nach einer Weile sieht man so viele verschiedene Stile, dass man sie einfach ignoriert. Wenn Sie versuchen würden, einen Stil durchzusetzen, würden Sie ständig alles
Ewan,
4
Wenn Sie "Sie können falsch und daher irreführend sein" auf fast alles anwenden, stellen Sie schnell fest, dass Sie keine Variablen oder Funktionen benennen sollten - sie könnten schließlich falsch sein! Und ich vermute, dass Ihr zweiter Aufzählungspunkt "Klassen, die Sie NICHT erstellt haben" sagen soll? Sonst verstehe ich es einfach nicht ... Auf jeden Fall hört es sich so an, als wäre ein einziger Unterstrich für nicht öffentliche Felder ein inoffizieller Standard. Das ist wahrscheinlich die Richtung, in die wir gehen werden.
Betty Crokker
Einige Dinge werden vom Compiler geprüft, daher ist m_count möglicherweise keine Mitgliedsvariable, aber this.count ist immer
Ewan
Viele Leute benutzen _, aber die Dinge ändern sich im Laufe der Zeit. Sie werden feststellen, dass der Code, der letztes Jahr nach den besten Praktiken geschrieben wurde, nicht den diesjährigen Konventionen entspricht
Ewan
4

Für mich, wenn ich den Code einer anderen Person durchlese und Folgendes sehe:

count = 3;

Ich gehe davon aus, dass 'count' eine lokale Variable für diese Funktion ist und mache etwas, das an anderer Stelle in der Funktion verwendet wird

Und Sie werden absolut Recht haben, wenn Sie Quellcode lesen, der die Stilkonventionen von C # respektiert. In einem solchen Code:

this.count = 3;

Weist einem Feld einen Wert zu.

count = 3;

Weist einer lokalen Variablen einen Wert zu.

Daher sind die Stilkonventionen von C # klar und eindeutig. Sie zwingen Sie, kein Präfix zu verwenden, nur weil Sie keines benötigen.

Bezüglich des Quellcodes, für den die Autoren ihre persönlichen Konventionen verwenden, sollten Sie sie persönlich fragen , warum sie sich dafür entschieden haben. Wenn sie keine Präfixe wie Unterstriche verwenden, obwohl sie keine verwenden this., führt dies effektiv nicht nur zu einer schwierigen Codelesung, sondern auch zu Fällen, in denen eine zusätzliche Konvention erforderlich wäre:

public class Hello
{
    private string greetings;

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

Hat C # das Schlüsselwort this nicht? Könnten Sie nicht auf Mitglieder verweisen, die dies verwenden, wie zum Beispiel this.count?

Ja, aber (1) es ist mehr Tippen und (2) es ist leicht zu vergessen. Wenn das Feld m_count heißt, muss ich nicht daran denken, this.m_count einzugeben

Hier liegen Sie jedoch falsch.

Es ist typischer, wenn Sie Ihren Code in Notepad schreiben. Bei einer IDE mit automatischer Vervollständigung oder besser IntelliSense ist der Vergleich zwischen this.countund m_countnicht so offensichtlich. Beachten Sie das Shift+ -, um den Unterstrich einzugeben.

Was wiederum "leicht zu vergessen" betrifft, ist dies nur für die Benutzer von Notepad relevant. Nicht nur StyleCop und Resharper lassen Sie das Setzen this.bei Bedarf nicht vergessen , sondern nach ein paar Stunden Programmierzeit wird das Setzen this.bei Bedarf zur Gewohnheit.

Arseni Mourzenko
quelle
6
Ich finde, dass die Verwendung thisnur dann, wenn Sie es brauchen, zu einem wirklich inkonsistenten Gefühl führt und mich viel zu oft ablenkt. Wenn Sie thisanstelle eines Präfixes verwenden, sollten Sie es meines Erachtens immer verwenden. In diesem Fall wird es zu einem Präfix, das Sie genauso gut verwenden können _.
RubberDuck
1
RubberDuck ist richtig. "Dies." ist ein Präfix, obwohl es eine Compiler-Bedeutung hat.
Frank Hileman
4

Das Präfix "member" ist eine Art Implementierungsdetail. Es lenkt Ihre Aufmerksamkeit von der Problemdomäne auf die Domäne der technischen Lösung ab, die Sie bei der Überlegung möglicher Umgestaltungen in den Bann zieht. - mich

kannst du weiter erklären Dein ist der einzige Grund, warum ich das Präfix nicht benutzen kann und ich verstehe es nicht ... (womit ich meine, die anderen Dinge, die die Leute sagen, sind Gründe, warum ich es nicht benutzen muss, was nicht das gleiche ist, warum ich nicht sollte) - Betty Crokker

Mein Punkt ist die Erweiterung der Antworten von @CandiedOrange und @Ewan.

Präfixe erschweren das Refactoring

Während sich Ihr Code weiterentwickelt, können Variablen und Methoden ihren Gültigkeitsbereich ändern. Z.B. Sie können eine private Methode erstellen und später feststellen, dass sie auch anderen (neuen) Klassen zur Verfügung stehen sollte. Ähnliches kann bei Methodenparametern und lokalen Variablen vorkommen.

Angenommen, Sie erstellen einen Steuerrechner. Gemäß dem Prinzip des Sichtbarkeitsbereichs von Mietverträgen für Variablen beginnen Sie mit einer Methode, die sowohl den Steuersatz als auch den Basiswert als Parameter verwendet:
(Beispiel sieht möglicherweise Java-isch aus ...)

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

Als nächstes müssen Sie den umgekehrten Vorgang implementieren und laut TDD tun Sie dies mit dem geringsten Aufwand:

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

Als nächstes möchte TDD, dass Sie den Code überarbeiten, um ihn übersichtlicher zu gestalten, wodurch die Parameterliste beider Methoden reduziert wird.
(Ja, Sie würden zuerst die doppelte Berechnung extrahieren, aber lassen Sie mich meinen Punkt verstehen ...)
Die naheliegende Lösung besteht darin, den Steuersatz in eine Mitgliedsvariable zu ändern, indem Sie ihn als Konstruktorparameter übergeben.

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

Wie Sie sehen, mussten wir alle Zeilen unserer vorhandenen Methoden ändern (jede Zeile, die wir früher p_TaxRateInpercentgenau angegeben haben).

Das Problem ist, dass Sie keine Unterstützung von Ihrer IDE haben , um das Umbenennen in der gesamten Klasse durchzuführen. Die einzige Hilfe beim Suchen / Ersetzen, die auch den Konstruktor oder einen Teil ändert, der die Zeichenfolge versehentlich enthält p_TaxRateInpercent.
Ihre IDE bietet möglicherweise eine Änderung an ... Refactoring für nicht definierte Variablennamen an, dies kann jedoch auf den Umfang der Methode beschränkt sein

Ohne das Präfix hätten sich nur die Methodensignaturen geändert. Es wäre überhaupt keine Umbenennung erforderlich gewesen.


Präfixe überladen den SCM-Verlauf, wenn sie geändert werden

Auch der SCM zeichnet die Änderung des Präfixes als Änderungen in der Logik auf, obwohl sich die Logik nicht geändert hat! Die Geschichte der SCMs ist durch diesen technischen Wandel geprägt, der das Wesentliche verbirgt und das Risiko von Konflikten mit den (realen logischen) Veränderungen anderer erhöht.

Timothy Truckle
quelle
1

Ich benutze _ Präfix für alle Mitgliedsvariablen. Ich hasse 'dieses' Präfix, weil es, während einige behaupten, die richtige Vorgehensweise ist, Ihrer Codebasis viel Lärm / Unordnung hinzufügt. Ein einzelner Unterstrich ist auch in Microsoft-Beispielen üblich.

In Ihrem Beispiel hätte ich also:

int _count = 10;
Eternal21
quelle