webentwicklung-frage-antwort-db.com.de

Initialisierung von C # -Mitgliedervariablen; beste Übung?

Ist es besser, Klassenmitgliedsvariablen bei der Deklaration zu initialisieren?

private List<Thing> _things = new List<Thing>();
private int _arb = 99;

oder im Standardkonstruktor?

private List<Thing> _things;
private int _arb;

public TheClass()
{
  _things = new List<Thing>();
  _arb = 99;
}

Ist es einfach eine Frage des Stils oder gibt es Performance-Kompromisse auf die eine oder andere Weise?

91
Steve Crane

In Bezug auf die Leistung gibt es keinen wirklichen Unterschied. Feldinitialisierer werden als Konstruktorlogik implementiert. Der einzige Unterschied besteht darin, dass Feldinitialisierer vor jedem "base"/"this" -Konstruktor auftreten.

Der Konstruktoransatz kann mit automatisch implementierten Eigenschaften verwendet werden (Feldinitialisierer können dies nicht) - d. H.

[DefaultValue("")]
public string Foo {get;set;}
public Bar() { // ctor
  Foo = "";
}

Ansonsten bevorzuge ich die Syntax des Feldinitialisierers. Ich finde, es hält die Dinge lokalisiert - d. H.

private readonly List<SomeClass> items = new List<SomeClass>();
public List<SomeClass> Items {get {return items;}}

Ich muss nicht auf die Jagd gehen, um herauszufinden, wo es zugewiesen ist ...

Die offensichtliche Ausnahme besteht darin, dass Sie komplexe Logik ausführen oder sich mit Konstruktorparametern befassen müssen. In diesem Fall ist die konstruktorbasierte Initialisierung die richtige Wahl. Wenn Sie mehrere Konstruktoren haben, ist es ebenfalls vorzuziehen, dass die Felder immer auf die gleiche Weise gesetzt werden - daher könnten Sie ctors wie folgt haben:

public Bar() : this("") {}
public Bar(string foo) {Foo = foo;}

edit: Beachten Sie als Nebenkommentar, dass im obigen Beispiel, wenn es andere Felder (nicht gezeigt) mit Feldinitialisierern gibt, diese nur direkt in den Konstruktoren initialisiert werden, die base(...) aufrufen - dh die public Bar(string foo) ctor. Der andere Konstruktor führt nicht Feldinitialisierer aus, da er weiß, dass sie von der Funktion this(...) ctor ausgeführt werden.

76
Marc Gravell

Tatsächlich sind Feldinitialisierer, wie Sie demonstrieren, eine praktische Abkürzung. Der Compiler kopiert den Initialisierungscode tatsächlich an den Anfang jedes Instanzkonstruktors, den Sie für Ihren Typ definieren.

Dies hat zwei Auswirkungen: Erstens wird jeder Feldinitialisierungscode in jedem Konstruktor dupliziert, und zweitens wird jeder Code, den Sie in Ihren Konstruktoren zum Initialisieren von Feldern auf bestimmte Werte einfügen, die Felder tatsächlich neu zuweisen.

In Bezug auf die Leistung und die kompilierte Codegröße ist es daher besser, Feldinitialisierer in Konstruktoren zu verschieben.

Andererseits sind die Auswirkungen auf die Leistung und das Aufblähen des Codes in der Regel vernachlässigbar, und die Feldinitialisierungssyntax hat den wichtigen Vorteil, dass das Risiko verringert wird, dass Sie möglicherweise vergessen, ein Feld in einem Ihrer Konstruktoren zu initialisieren.

9
Tor Haugen

Eine wesentliche Einschränkung bei Feldinitialisierern besteht darin, dass sie nicht in einen try-finally-Block eingeschlossen werden können. Wenn in einem Feldinitialisierer eine Ausnahme ausgelöst wird, werden alle Ressourcen, die in früheren Initialisierern zugewiesen wurden, verworfen. Es gibt keine Möglichkeit, dies zu verhindern. Andere Konstruktionsfehler können umgangen werden, indem ein Konstruktor mit geschützter Basis ein IDisposable als Referenz akzeptiert und es als erste Operation auf sich selbst zeigt. Man kann dann vermeiden, den Konstruktor aufzurufen, außer durch Factory-Methoden, die im Ausnahmefall Dispose für das teilweise erstellte Objekt aufrufen. Dieser Schutz ermöglicht die Bereinigung von IDisposables, die in Initialisierern abgeleiteter Klassen erstellt wurden, wenn der Hauptklassenkonstruktor nach dem "Herausschmuggeln" eines Verweises auf das neue Objekt fehlschlägt. Leider gibt es keine Möglichkeit, einen solchen Schutz bereitzustellen, wenn ein Feldinitialisierer ausfällt.

4
supercat

Verwenden Sie entweder Feldinitialisierer oder erstellen Sie eine Init () - Funktion. Das Problem beim Einfügen dieser Dinge in Ihren Konstruktor besteht darin, dass Sie, wenn Sie jemals einen zweiten Konstruktor hinzufügen müssen, Code kopieren/einfügen müssen (oder diesen übersehen und nicht initialisierte Variablen erhalten).

Ich würde entweder initialisieren, wo deklariert. Oder lassen Sie die Konstruktoren eine Init () - Funktion aufrufen.

2
GeekyMonkey

Zum Beispiel Variablen, es ist weitgehend eine Frage des Stils (ich bevorzuge die Verwendung eines Konstruktors). Für statische Variablen gibt es ein Leistungsvorteil für die Inline-Initialisierung (natürlich nicht immer möglich).

1
Kent Boogaart

Ein weiterer Punkt: Sie haben immer einen Konstruktor, wenn Sie Klassen mit einer Implementierung implementieren. Wenn Sie keinen deklarieren, wird der Standardausbilder vom Compiler abgeleitet [public Foo () {}]; Ein Konstruktor, der keine Argumente akzeptiert.

Oft biete ich gerne beide Ansätze an. Lassen Sie Konstruktoren für diejenigen zu, die sie verwenden möchten, und ermöglichen Sie den Feldinitialisierern für Situationen, in denen Sie eine vereinfachte oder Standardimplementierung Ihrer Klasse/Ihres Typs verwenden möchten. Dies erhöht die Flexibilität Ihres Codes. Denken Sie daran, dass jeder Benutzer den Standard-Feldinitialisierer verwenden kann, wenn er wählt ... Sie müssen ihn manuell deklarieren, wenn Sie mehr als einen Konstruktor anbieten - public Foo () {}

0
Qubits

Es liegt wirklich an dir.
Ich initialisiere sie oft inline, weil ich keinen Konstruktor mag, wenn ich keinen wirklich brauche (ich mag kleine Klassen!).

0
Nico