12 Stimmen

ist es schlecht, einen Initialisierungsblock zu verwenden

Hallo Ich verwende Initialisierungsblock in C#

new Something { foo = 1, bar = 2 };

aber die Leute sagen, das sei eine schlechte Praxis.

Ich denke nicht, dass es falsch ist, oder?

10voto

Jon Skeet Punkte 1325502

Sie müssen sich fragen, ob Ihr Typ veränderbar sein soll oder nicht. Ich persönlich mag unveränderliche Typen - sie machen es einfacher, darüber nachzudenken, was vor sich geht, sie sind einfacher zu validieren (sobald der Konstruktor aufgerufen und der Zustand validiert wurde, wissen Sie, dass er nicht ungültig werden wird) und sie sind großartig für die Parallelität.

Andererseits sind Objektinitialisierer sicherlich in Fällen nützlich, in denen es ist vernünftig, veränderbare Typen zu haben. Als ein Beispiel, ProcessStartInfo wird effektiv als Builder-Typ für Process . Es ist nützlich, schreiben zu können:

var info = new ProcessStartInfo { 
    FileName = "notepad.exe",
    Arguments = "foo.txt",
    ErrorDialog = true
};
Process process = Process.Start(info);

Sie können all dies sogar inline tun, anstatt eine zusätzliche Variable zu haben. Mein Protocol Buffers Port verwendet die gleiche Art von Muster:

Foo foo = new Foo.Builder { 
    FirstProperty = "first",
    SecondProperty = "second"
}.Build();

Eine Alternative zum Builder-Muster sind Konstruktorparameter (möglicherweise über Factory-Methoden). Der historische Nachteil dabei ist, dass man verschiedene Überladungen benötigte, je nachdem, welche Eigenschaften eingestellt wurden, und wenn mehrere Parameter denselben Typ hatten, konnte es schwierig sein zu erkennen, welcher welcher war. In C# 4 wird dies durch optionale Parameter und benannte Argumente erheblich vereinfacht. Wenn Sie z. B. eine E-Mail-Klasse erstellen, können Sie Folgendes verwenden:

Email email = new Email(
    from: "skeet@pobox.com",
    to: "jon@example.com",
    subject: "Test email",
    body: textVariable
);

Dies hat viele der gleichen Vorteile von Objektinitialisierungen in Bezug auf Klarheit, aber ohne den Nachteil der Veränderbarkeit. Der obige Konstruktoraufruf hat möglicherweise einige optionale Parameter wie Anhänge und eine BCC-Liste ausgelassen. Ich denke, dies wird sich als einer der größten Vorteile von C# 4 für diejenigen von uns erweisen, die die Unveränderlichkeit, aber auch die Klarheit von Objektinitialisierern mögen.

8voto

Aaronaught Punkte 118136

Es ist eine fragwürdige (ich will nicht sagen "schlechte") Praxis, Initialisierungsblöcke zu verwenden als Ersatz für die entsprechende Konstruktorüberladung wenn eine solche existiert.

public class Entity
{
    public Entity()
    {
    }

    public Entity(int id, string name)
    {
        this.ID = id;
        this.Name = name;
    }

    public int ID { get; set; }
    public string Name { get; set; }
}

Wenn Sie diese sehr einfache Klasse haben, dann ist es im Allgemeinen besser, sie zu schreiben:

var entity = new Entity(1, "Fred");

...als es zu schreiben ist:

var entity = new Entity { ID = 1, Name = "Fred" };

Hierfür gibt es mindestens zwei gute Gründe:

  1. Sie wissen nicht genau, was der Konstruktor tut. Es ist möglich, dass es unter bestimmten Umständen wesentlich teurer sein kann, das Objekt zu konstruieren und dann öffentliche Eigenschaften zu setzen, anstatt die Werte durch den Konstruktor selbst zu übergeben. (Sie wissen vielleicht, dass dies nicht der Fall ist, aber als Konsument einer Klasse sollten Sie sich nicht anmaßen, sich um die Implementierungsdetails zu kümmern, da sie sich ändern können).

  2. Ihr Code wird nicht abbrechen, wenn eine oder mehrere dieser Eigenschaften ihren Namen geändert haben oder schreibgeschützt werden (was die ID wahrscheinlich von vornherein hätte sein sollen, es aber aufgrund von architektonischen Einschränkungen wie denen eines ORM nicht war).

Es gibt jedoch einen Fall, in dem Sie 持つ Initialisierer anstelle von überladenen Konstruktoren zu verwenden, und zwar beim Verketten von Selects in einer Linq to SQL/EF-Abfrage:

var bars =
    from f in ctx.Foo
    select new Bar { X = f.X, Y = f.Y };
var bazzes =
    from b in bars
    select new Baz { ... };

Dies kann tatsächlich mit einem "no supported mapping" fehlschlagen, wenn Sie Konstruktorüberladungen anstelle von Standardkonstruktoren + Initialisierungen verwenden. Dies ist jedoch eine Einschränkung der verwendeten Technologie (und eine unerwünschte noch dazu) und kein Problem des Codierungsstils.

Sur andere Fällen sollten Sie die Überladung des Konstruktors dem Initialisierer vorziehen.

Wenn es keine sinnvolle/relevante Konstruktorüberladung gibt die dasselbe tun können wie Ihr Initialisierer, dann machen Sie weiter und schreiben Sie den Initialisierer, es ist nichts falsch mit ihm. Die Funktion gibt es aus einem guten Grund - sie macht den Code einfacher zu schreiben y lesen.

4voto

Konrad Rudolph Punkte 503837

aber die Leute sagen, das sei schlechte Praxis.

Wer sagt das? Zumindest ist das eine kontroverse Aussage.

Es scheint im Moment der letzte Schrei zu sein, und viele prominente C#-Blogs verwenden es ausgiebig.

Der Vorteil gegenüber der Verwendung eines Konstruktors ist die bessere Lesbarkeit, da der Code deutlich zeigt, welchen Eigenschaften welche Werte zugewiesen werden. Vergleichen Sie:

var x = new Something { Foo = 1, Bar = 2 };

mit

var x = new Something(1, 2);

Wenn kein entsprechender Konstruktor vorhanden ist, ist der Code außerdem übersichtlicher als die manuelle Zuweisung von Eigenschaften:

var x = new Something();
x.Foo = 1;
x.Bar = 2;

Ich persönlich bevorzuge unveränderliche Objekte (d. h. Objekte, die nach ihrer Erstellung nicht mehr geändert werden können). Leider können die Initialisierungsblöcke nicht in Verbindung mit solchen Objekten verwendet werden ( zur Zeit ), denn damit dieses Muster funktioniert, muss das Objekt Eigenschaftssetzer haben, was bei einem unveränderlichen Objekt nicht der Fall ist.

Solange das verwendete Objekt jedoch nicht unveränderlich ist, sehe ich keinen zwingenden Grund, der gegen die Verwendung der Initialisierungsschreibweise spricht.

3voto

Danny Varod Punkte 16437

Initialisierungsblöcke sind aus den folgenden Gründen eine GREAT-Praxis:

  1. Sie müssen ein Objekt erstellen und seine Eigenschaften außer Kraft setzen, bevor Sie seine Referenz erhalten

    this.ObjA = new ObjA
    {
        Age = 20,
        Name = "123",
    };
    
    // this.ObjA will not be set until properties have all been defined
    // - safer when multithreading
  2. Der parameterlose Konstruktor kann immer noch Dinge hinter der Bühne tun tun (z.B. Zustandsmitglieder initialisieren).

  3. Sie können in Verbindung mit Konstruktoren mit Parametern verwenden

    this.ObjA = new ObjA(20)
    {
        Name = "123",
    };
  4. Die Verwendung parameterloser Konstruktoren ist besser für (De-)Serialisierungsszenarien

    Sie können verschiedene Objekte erstellen, ihren Zustand über die GUI ändern, sie serialisieren und an anderer Stelle deserialisieren.

  5. Diese Praxis zwingt die Autoren dazu, robusteren Code zu schreiben, bei dem die Reihenfolge, in der die Dinge erledigt werden, weniger leicht zu einem Absturz der Anwendung führt, wenn die Metadaten der Klasse geändert werden.

1voto

Brian R. Bondy Punkte 325712

Gegen Initialisierungsblöcke ist nichts einzuwenden, aber wenn Ihr Typ zum Beispiel viele Eigenschaften hat und nur ein paar davon bei jeder Instanz gesetzt werden müssen, dann sollten Sie diese in einem Konstruktor erforderlich machen.

Der Benutzer Ihrer Klasse wird wissen, dass er ohne Angabe dieser Werte kein Objekt erstellen kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X