2010-05-10 6 views
11

Mögliche Duplizieren zu initialisieren: über Codierpraktiken
Initialize class fields in constructor or at declaration?Ist es schlechte Praxis Bereichen außerhalb eines expliziten Konstruktor

Wir streiten. Die Beispiele hier sind ein wenig zu einfach, aber das wirkliche Geschäft hat mehrere Konstruktoren. Um die einfachen Werte (z. B. Datumswerte auf ihren minimalen Wert) zu initialisieren, habe ich den Code aus den Konstruktoren in die Felddefinitionen verschoben.

public class ConstructorExample 
{ 
    string _string = "John"; 
} 

public class ConstructorExample2 
{ 
    string _string; 

    public ConstructorExample2() 
    { 
     _string = "John"; 
    } 
} 

Wie sollte es durch das Buch getan werden? Ich tendiere dazu, von Fall zu Fall sehr viel zu sein und bin daher vielleicht etwas lax über diese Art von Dingen. Ich denke jedoch, dass der Occams-Rasierer mir sagt, ich solle die Initialisierung von mehreren Konstruktoren entfernen. Natürlich könnte ich diese gemeinsame Initialisierung immer in eine private Methode umsetzen.

Die Frage ist im Wesentlichen ... ist die Initialisierung von Feldern, wo sie im Gegensatz zu dem Konstruktor schlecht in irgendeiner Weise definiert sind?

Das Argument, mit dem ich konfrontiert bin, ist einer der Fehlerbehandlung, aber ich denke nicht, dass es relevant ist, da es keine möglichen Ausnahmen gibt, die zur Kompilierzeit nicht aufgenommen werden.

+0

mögliche Duplikate von [Best Practice: Klassenfelder im Konstruktor oder bei der Deklaration initialisieren?] (Http://stackoverflow.com/questions/2451/best-practice-initialize-class-fields-in-constructor-or-at -declaration) –

+1

es ist, aber ich finde die Antworten in diesem Thread nicht überzeugend sein - das erste ist ein wenig dogmatisch. Der Verweis auf FXCop ist ein starkes Argument, ebenso wie das Maintenance- und Konstruktor-Chaining-Argument, aber abgesehen davon nichts zu sachliches. –

+3

Für einige interessante Konsequenzen der feinen Unterschiede zwischen der Initialisierung im Feld und im Körper, siehe http://blogs.msdn.com/ericlippert/archive/2008/02/15/why-do-initializers-run-in-in- die-entgegengesetzte-Reihenfolge-als-Konstruktoren-Teil-One.aspx und http://blogs.msdn.com/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite- order-as-constructors-part-two.aspx –

Antwort

13

Es ist nicht unbedingt schlecht Werte zu initialisieren außerhalb des Konstrukteurs, und das Problem haben Sie hier:

string _string; 

    public ConstructorExample2() 
    { 
     _string = "John"; 
    } 

Ist das, wenn Sie mehrere Konstrukteure haben Sie sich merken müssen entweder
1. Reinitialize _string Jeder Konstruktor
2. Trennen Sie die Logik in eine allgemeine Methode und rufen Sie diese Methode in jedem Konstruktor
3. Rufen Sie den Konstruktor mit der Logik in ihm, aus den anderen Konstruktoren. (Verketten Sie die Konstruktoren)
Nun ist dies nicht unbedingt ein Problem, aber Sie müssen daran denken, es zu tun. Indem es außerhalb des Konstruktors initialisiert wird, ist es für Sie erledigt. Es ist eine Sache weniger, an die du dich erinnern musst.

+7

Sie müssen den _string nicht in jedem Konstruktor neu initialisieren. Stattdessen können Sie ctors mit einer public MyClass() verketten: this (String.Leeren) ' – Oliver

+0

Mit dem Kommentar, ich mag dies als eine ausgewogene Antwort. –

+0

Oliver kümmern sich um einige Überlegungen? –

0

Ich bevorzuge einfache Felder wie diese außerhalb des Konstruktors zu initialisieren.

Es sollte keine Probleme verursachen, da die Kompilierung diese Initialisierungen ohnehin zur Kompilierungszeit in den Konstruktor verschiebt.

2

Microsoft FxCop empfiehlt standardmäßig Feldinitialisierungen über die Verwendung des Konstruktors. This question ist auch ein Duplikat von diesem und sollte einige Einblicke bieten.

Bei statischen Klassen müssen Sie einige Feinheiten beachten, wie unter this question adressiert.

+0

Warum macht FxCop diese Empfehlung? –

+0

@Robert: Ich bin froh, dass du gefragt hast. Ich habe meine Antwort aktualisiert, um einige unterstützende Dokumente in Form anderer SO-Fragen/Antworten zu enthalten. Ich gehe davon aus, dass die Feldinitialisierer vor Konstruktoren ausgeführt werden und den Konstruktoren ein "mehr bereites" Objekt zum Arbeiten geben. –

+2

Mein Fehler - scheint wie FxCop empfiehlt nur im Falle von statischen Klassen: http://msdn.microsoft.com/en-us/library/ms182275.aspx –

0

Wenn die Initialisierung der Variablen gleich ist, egal welche Argumente an den Konstruktor übergeben werden, dann macht es keinen Sinn, die Konstruktormethode mit dem unnötigen Initialisierungscode zu verwischen. In diesem Fall initialisiere ich direkt.

0

Das Inisialisieren der Felder im Konstruktor ist besser. Wenn Sie also einen anderen Konstruktor hinzufügen, wissen Sie, dass alle Felder mit Null-/Standardwerten beginnen und Sie diese entsprechend initialisieren können.

1

Im obigen Beispiel hat die Zuweisung von "John" zu _string keine logische Abhängigkeit von Variablen und sollte daher außerhalb des Konstruktors im Feldinitialisierer liegen.

Solange es nicht möglich ist, das Objekt in einem nicht verwendbaren Zustand zu initialisieren, spielt es keine Rolle.

Wenn der Code kompiliert wird, werden beide Ansätze gleich sein.

15

Beachten Sie, dass alle solche Initialisierungen der Felddeklarationsebene für jede Konstruktorkette einmal ausgeführt werden, auch wenn der Konstruktor das Feld selbst auf etwas anderes setzt.

Wenn Sie Konstruktoren zusammenketten, werden die Felder im allgemeinen ersten Konstruktor initialisiert, der aufgerufen wird.

Schauen Sie sich das Beispiel:

using System; 

namespace ClassLibrary3 
{ 
    public class Class1 
    { 
     private string _Name = "Lasse"; 

     public Class1() 
     { 
     } 

     public Class1(int i) 
      : this() 
     { 
     } 

     public Class1(bool b) 
     { 
      _Name = "Test"; 
     } 
    } 
} 

Dieser Code kompiliert wie folgt aus:

using System; 

namespace ClassLibrary3 
{ 
    public class Class1 
    { 
     private string _Name; 

     public Class1() 
     { 
      _Name = "Lasse" 
     } 

     public Class1(int i) 
      : this() 
     { 
      // not here, as this() takes care of it 
     } 

     public Class1(bool b) 
     { 
      _Name = "Lasse" 
      _Name = "Test"; 
     } 
    } 
} 
+1

Ich wünschte, ich könnte Sie mehr als einmal beleidigen. ;-) – Oliver

+0

Ja, Tut mir leid, ich konnte das nicht als Antwort markieren. Der Punkt, dass Initialisierer in einer Reihenfolge aufgerufen werden, die anfänglich für mich als gegensinnig empfunden wurde, ist ziemlich stark - besonders in Vererbungsszenarien und wenn man weiß, was initialisiert wurde und was nicht. –

1

über C# nicht sicher, aber in Java-Quellcode scheinen sie den Konstruktor, Beispiel zu bevorzugen:

public class String{ 
    char[] value; 
    int offset; 
    ... 
    public String(){ 
     value = new char[0]; 
     offset = 0; 
     ... 
    } 
} 
+0

Ich streite mit ac/Java-Programmierer;) Es gibt eine Möglichkeit, in Java so zu initialisieren http://java.sun.com/docs/books/tutorial/java/javaOO/initial.html –

+0

Ich weiß, dass es gibt, aber dieser Ausschnitt stammt vom Java-Quellcode selbst. Normalerweise, wenn ich etwas verdächtige, überprüfe ich, wie die großen Köpfe, die Java geschrieben haben, es gelöst haben. – medopal

1

Ich denke für einfache Initialisierungen wie das ist es in Ordnung, es in der Deklaration zu tun. Allerdings verstehe ich das Fehlerbehandlungsargument nicht. Selbst wenn es eine Ausnahme bei der Initialisierung gibt, werden Sie feststellen, dass Ihr normaler Fehlerbehandlungsmechanismus genauso funktioniert. Es wird weiterhin eine Ausnahme ausgelöst, wenn Sie den Konstruktor aufrufen.

+0

Ich stimme dem zu, möchte es aber nicht von der Hand weisen. –

1

Ich neige dazu, Dinge im Get Accessor zu initialisieren, wo sie zuerst verwendet werden. Wenn null, dann initialisiere und all das.

+0

Ich hatte diese Option nicht berücksichtigt. Das einzige Problem ist in dem Fall, wenn ein Nullwert ein gültiger Wert ist, aber nicht der Standardwert ist. –

+0

Sie können immer einen booleschen Wert verwenden, um den initialisierten Zustand anzuzeigen, anstatt sich auf "if (value == null) {initialize;}" zu verlassen. Ich mag die Initialisierung beim Zugriff - ich denke, es wird häufiger als "träge Initialisierung" bezeichnet. Es ist sehr praktisch, wenn Sie mit einer Datenbank arbeiten. Es macht es auch leicht, Dinge zu cachen, wenn Sie daran interessiert sind. – quillbreaker

+0

Ich glaube nicht, dass ich diesen Ansatz für einen Literalwert-Initialisierer (wie eine literale Zeichenfolge) verwenden würde. Es handelt eine einmalige Zuweisung mit einem Test für null bei jedem get und macht den Code komplexer. Für andere Szenarien, insbesondere bei großen oder langsamen Objektinitialisierungen, könnte dies sinnvoll sein. –

Verwandte Themen