2009-06-04 8 views
4

Es ist einfach, ungerade Zahlen wie 0, 1 oder 5 zu verfolgen. Ich war damals sehr streng, als ich Low-Level-C-Code schrieb. Da ich mehr mit allen String-Literalen arbeite, die mit XML und SQL zu tun haben, breche ich oft die Regel der Einbettung von Konstanten in Code, zumindest wenn es sich um String-Literale handelt. (Ich bin immer noch gut über numerische Konstanten.)Werden String-Konstanten überbewertet?

Strings sind nicht das Gleiche wie Zahlen. Es ist mühsam und ein wenig albern, eine Kompilierzeitkonstante zu erzeugen, die den gleichen Namen wie ihr Wert hat (zB const string NameField = "Name";), und obwohl die Wiederholung des gleichen Zeichenkettenliterals an vielen Stellen riskant erscheint, gibt es kaum eine Chance auf einen Tippfehler Kopieren und Einfügen, und wenn ich umgestalte, mache ich normalerweise eine globale Suche, bei der mehr als nur der Name der Sache geändert wird, etwa wie sie funktional in Bezug auf die Dinge behandelt wird.

Nehmen wir an, Sie haben keinen guten XML-Serializer (oder sind nicht in der Stimmung, einen zu erstellen). Welche davon würden Sie persönlich verwenden (wenn Sie nicht auf Peer-Druck in einigen Code-Review zu beugen versuchten):

static void Main(string[] args) 
{ 
    // ...other code... 

    XmlNode node = ...; 

    Console.WriteLine(node["Name"].InnerText); 
    Console.WriteLine(node["Color"].InnerText); 
    Console.WriteLine(node["Taste"].InnerText); 

    // ...other code... 
} 

oder:

class Fruit 
{ 
    private readonly XmlNode xml_node; 

    public Fruit(XmlNode xml_node) 
    { 
     this.xml_node = xml_node; 
    } 

    public string Name 
    { get { return xml_node["Name"].InnerText; } } 

    public string Color 
    { get { return xml_node["Color"].InnerText; } } 

    public string Taste 
    { get { return xml_node["Taste"].InnerText; } } 
} 

static void Main(string[] args) 
{ 
    // ...other code... 

    XmlNode node = ...; 
    Fruit fruit_node = new Fruit(node); 

    Console.WriteLine(fruit_node.Name); 
    Console.WriteLine(fruit_node.Color); 
    Console.WriteLine(fruit_node.Taste); 

    // ...other code... 
} 
+2

0 ist keine ungerade Zahl, es ist sogar :-) – paxdiablo

Antwort

0

Wie Sie wahrscheinlich erraten. Die Antwort ist: Es hängt vom Kontext ab.

Es hängt davon ab, wozu der Beispielcode gehört. Wenn es nur ein Teil eines kleinen Wegwerfsystems ist, dann kann eine harte Codierung der Konstanten akzeptabel sein.

Wenn es Teil eines großen, komplexen Systems ist und die Konstanten in mehreren Dateien verwendet werden, würde ich eher von der zweiten Option angezogen werden.

0

Wie in vielen Fragen der Programmierung ist dies eine Frage des Geschmacks. Die "Gesetze" der richtigen Programmierung wurden aus Erfahrung erstellt - viele Menschen wurden durch globale Variablen gebrannt, die Namensraum- oder Klarheitsprobleme verursachten, also sind globale Variablen böse. Viele haben magische Zahlen benutzt, um später festzustellen, dass die Zahl falsch war oder geändert werden musste. Die Textsuche ist für das Ändern dieser Werte ungeeignet, daher sind Konstanten im Code böse.

Aber beide sind erlaubt, weil sie manchmal nicht böse sind. Sie müssen die Entscheidung selbst treffen - was zu klareren Code führt? Was wird für Betreuer besser? Gilt die Begründung der ursprünglichen Regel für meine Situation? Wenn ich diesen Code später lesen oder pflegen müsste, wie würde ich dann lieber schreiben?

Es gibt kein absolutes Gesetz des guten Kodierungsstils, weil keine Gedanken von zwei Programmierern genau gleich funktionieren. Die Regel ist, den klarsten, saubersten Code zu schreiben, den Sie können.

0

Persönlich würde ich die Frucht aus der XML-Datei im Voraus geladen werden - so etwas wie:

public class Fruit 
{ 
    public Fruit(string name, Color color, string taste) 
    { 
     this.Name = name; this.Color = color; this.Taste = taste; 
    } 

    public string Name { get; private set; } 
    public Color Color { get; private set; } 
    public string Taste { get; private set; } 
} 

// ... In your data access handling class... 
    public static FruitFromXml(XmlNode node) 
    { 
      // create fruit from xml node with validation here 
    } 
} 

diese Weise die „Frucht“ ist nicht wirklich an die Speicher gebunden.

+0

Guter Rat, aber keine Antwort auf die Frage. – jmucchiello

+0

@jmucchiello: Ja und Nein - Es ist die Antwort, in der ich auch nicht würde. Es ist nicht wirklich eine Konstante, es ist maskiertes Verhalten über einen Xml-Knoten "Abfrage" für einen Wert. Keine der beiden Optionen würde ich in meinem Code bevorzugen, also gebe ich an, was ich bevorzuge. –

5

Für so etwas hängt es davon ab, wie oft die Konstante verwendet wird. Wenn es nur an einem Ort ist, wie in Ihrem Beispiel, dann ist die Hard-Codierung in Ordnung. Wenn es an vielen verschiedenen Orten verwendet wird, verwenden Sie unbedingt eine Konstante. Ein Tippfehler könnte zu Stunden des Debuggens führen, wenn Sie nicht vorsichtig sind, weil Ihr Compiler nicht bemerkt, dass Sie "Tsate" anstelle von "Taste" eingegeben haben, während es bemerken wird, dass Sie fruit_node.Tsate anstelle von fruit_node eingegeben haben. Geschmack.

Edit: Ich sehe jetzt, dass Sie Kopieren und Einfügen erwähnt, aber wenn Sie das tun, verlieren Sie möglicherweise auch die Zeit, die Sie sparen, indem Sie eine Konstante überhaupt nicht erstellen. Mit Intellisense und Auto-Vervollständigung können Sie die Konstante in wenigen Tastenanschlägen zur Verfügung stellen, anstatt sich mit Copy/Paste zu befassen.

11

Eine definierte Konstante ist leichter zu refaktorieren. Wenn "Name" dreimal verwendet wird und Sie ihn in "FullName" ändern, wird die Konstante durch eine Änderung anstelle von drei geändert.

+0

Der Punkt des OP ist, dass ein globales Suchen und Ersetzen (in einem IDE oder einem Texteditor) alle drei in einem Vorgang erfasst. – Eddie

+3

@Eddie, es fängt auch jede andere Verwendung von "Name" const Zeichenfolge XmlNodeName = "Name"; const Zeichenfolge AddressName = "Name"; const Zeichenfolge XmlFruitName = "Name"; Was ist, wenn Sie "Name" überall fest codierten und nur der XmlNodeName änderte? –

+0

Yeah global suchen und ersetzen ist noch gefährlicher als mit hardcoded Strings. Vor allem, wenn sie hart codierte Saiten ersetzen. – Gerald

0

Ich würde mit den Konstanten gehen. Es ist ein wenig mehr Arbeit, aber es gibt keine Auswirkungen auf die Leistung. Und selbst wenn Sie die Werte normalerweise kopieren/einfügen, hatte ich sicherlich Fälle, in denen ich Code änderte, als ich tippte und nicht erkannte, dass Visual Studio den Fokus hatte. Ich würde viel bevorzugen diese resultierten Kompilierungsfehler.

0

Für das Beispiel gegeben, wo die Strings als Schlüssel zu einer Karte oder einem Wörterbuch verwendet werden, würde ich stattdessen auf die Verwendung eines enum (oder eines anderen Objekts) neigen. Sie können oft viel mehr mit einem Enum als mit einer konstanten Zeichenfolge tun. Wenn ein Code auskommentiert ist, werden IDEs dies oft übersehen, wenn ein Refactor ausgeführt wird. Verweise auf eine String-Konstante, die in Kommentaren enthalten sind, können auch in einem Refactor enthalten sein.

Ich mache eine Konstante für eine Zeichenfolge, wenn die Zeichenfolge an vielen Orten verwendet wird, die Zeichenfolge ist lang oder kompliziert (wie eine Regex), oder wenn eine richtig benannte Konstante den Code offensichtlicher macht.

Ich bevorzuge es, dass meine Tippfehler, unvollständige Refactorings und andere Fehler dieser Art nicht kompilieren, anstatt nur nicht ordnungsgemäß zu funktionieren.

0

Wie viele andere Refactorings ist es ein möglicherweise optionaler zusätzlicher Schritt, der Sie mit Code belässt, der weniger riskant zu warten ist und leichter vom "nächsten Typ" gerankt werden kann. Wenn du dich in einer Situation befindest, die so etwas belohnt (das meiste, was ich tue), dann tu es.

0

Ja, ziemlich.

Ich denke Entwickler in statisch getippten Sprachen haben eine ungesunde Angst vor allem Dynamischen. So ziemlich jede Codezeile in einer dynamisch typisierten Sprache ist effektiv ein String-Literal, und sie sind seit Jahren in Ordnung. Zum Beispiel in JavaScript technisch dies:

var x = myObject.prop1.prop2; 

entspricht dies:

var x = window["myObject"]["prop1"]["prop2"]; // assuming global scope 

Aber es ist auf jeden Fall nicht eine gängige Praxis in JavaScript, dies zu tun:

var OBJ_NAME = "myObject"; 
var PROP1_NAME = "prop1"; 
var PROP2_NAME = "prop2"; 

var x = window[OBJ_NAME][PROP1_NAME][PROP2_NAME]; 

Das wäre einfach lächerlich.

Es hängt aber immer noch davon ab, wie wenn eine Zeichenkette an verschiedenen Stellen verwendet wird und es ziemlich umständlich/hässlich ist zu tippen ("name" vs. "mein-benutzerdefinierter-name-x"), dann ist es wahrscheinlich wert eine Konstante, sogar innerhalb einer einzelnen Klasse (zu diesem Zeitpunkt ist es wahrscheinlich gut, innerhalb der Klasse intern konsistent zu sein und alle anderen Strings auch zu Konstanten zu machen).

Wenn Sie tatsächlich beabsichtigen, dass andere externe Benutzer mit dieser Bibliothek interagieren, ist es auch eine gute Idee, öffentlich zugängliche Konstanten zu definieren und zu dokumentieren, dass Benutzer diese verwenden sollten, um mit Ihrer Bibliothek zu interagieren. Eine Bibliothek, die über magische String-Konstanten interagiert, ist jedoch in der Regel eine schlechte Übung, und Sie sollten Ihre Bibliothek so gestalten, dass Sie keine magischen Konstanten verwenden müssen, um überhaupt mit ihr zu interagieren.

Ich denke in dem konkreten Beispiel, das Sie angegeben haben, wo die Strings relativ einfach zu schreiben sind und es vermutlich keine externen Benutzer Ihrer API gibt, die mit diesen Zeichenfolgenwerten arbeiten würden (dh sie sind nur für interne Datenmanipulation), lesbaren Code ist viel wertvoller als refactorable Code, also würde ich die Literale direkt inline setzen. Auch hier wird davon ausgegangen, dass ich Ihren genauen Anwendungsfall genau verstehe.

Eine Sache, die niemand zu bemerken schien, ist, dass sobald man eine Konstante definiert, wird ihr Umfang zu etwas, was man behalten und darüber nachdenken muss. Das hat eigentlich hat kosten, es ist nicht kostenlos wie jeder scheint zu denken. Betrachten Sie das:

Sollte es privat oder öffentlich in meiner Klasse sein? Was, wenn ein anderer Namespace/Paket den gleichen Wert benötigt, sollte ich jetzt die Konstante in eine globale statische Klasse von Konstanten extrahieren? Was, wenn ich es jetzt in anderen Assemblys/Modulen brauche, entziehe ich es weiter? All diese Dinge machen den Code weniger lesbar, schwieriger zu pflegen, weniger angenehm zu handhaben und komplizierter. Alles im Namen der Refactorability?

In der Regel treten diese "großen Refactorings" nie auf, und wenn sie dies tun, benötigen sie eine vollständige Neuschreibung mit allen neuen Strings. Und wenn Sie vor diesem großen Refactoring (wie im obigen Absatz) ein geteiltes Modul verwendet haben, das nicht diese neuen Zeichenfolgen hat, die Sie jetzt brauchen, was dann? Fügen Sie sie demselben gemeinsamen Konstantenmodul hinzu (was ist, wenn Sie keinen Zugriff auf den Code für dieses freigegebene Modul haben)? Oder behalten Sie sie lokal für Sie? In diesem Fall gibt es jetzt mehrere verstreute Repositorys mit String-Konstanten, die alle auf verschiedenen Ebenen liegen und das Risiko doppelter Konstanten über den gesamten Code aufkommen lassen? Sobald Sie zu diesem Punkt (und glauben Sie mir, ich habe es gesehen), wird Refactoring strittig, denn während Sie alle Ihre Nutzungen erhalten werden Ihre Konstanten, werden Sie andere Leute Gebräuche vermissen ihre Konstanten, obwohl diese Konstanten den gleichen logischen Wert wie Ihre Konstanten haben und Sie tatsächlich versuchen, alle zu ändern.

Verwandte Themen