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?
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?
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.
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:
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).
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.
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.
Initialisierungsblöcke sind aus den folgenden Gründen eine GREAT-Praxis:
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
Der parameterlose Konstruktor kann immer noch Dinge hinter der Bühne tun tun (z.B. Zustandsmitglieder initialisieren).
Sie können in Verbindung mit Konstruktoren mit Parametern verwenden
this.ObjA = new ObjA(20)
{
Name = "123",
};
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.
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.
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 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.