it-swarm.com.de

Neues Objekt erstellen oder jede Eigenschaft zurücksetzen?

 public class MyClass
    {
        public object Prop1 { get; set; }

        public object Prop2 { get; set; }

        public object Prop3 { get; set; }
    }

Angenommen, ich habe ein Objekt myObject von MyClass und muss seine Eigenschaften zurücksetzen. Ist es besser, ein neues Objekt zu erstellen oder jede Eigenschaft neu zuzuweisen? Angenommen, ich habe keine zusätzliche Verwendung mit der alten Instanz.

myObject = new MyClass();

oder

myObject.Prop1 = null;
myObject.Prop2 = null;
myObject.Prop3 = null;
29
Bells

Das Instanziieren eines neuen Objekts ist immer besser, dann haben Sie 1 Stelle, um die Eigenschaften (den Konstruktor) zu initialisieren, und können es einfach aktualisieren.

Stellen Sie sich vor, Sie fügen der Klasse eine neue Eigenschaft hinzu. Sie möchten lieber den Konstruktor aktualisieren, als eine neue Methode hinzuzufügen, die auch alle Eigenschaften neu initialisiert.

In einigen Fällen möchten Sie möglicherweise ein Objekt wiederverwenden. In einem Fall ist die Neuinitialisierung einer Eigenschaft sehr teuer und Sie möchten sie behalten. Dies wäre jedoch spezialisierter, und Sie hätten spezielle Methoden, um alle anderen Eigenschaften neu zu initialisieren. Selbst in dieser Situation möchten Sie manchmal immer noch ein neues Objekt erstellen.

49
gbjbaanb

Sie sollten es definitiv vorziehen, in den meisten Fällen ein neues Objekt zu erstellen . Probleme bei der Neuzuweisung aller Eigenschaften:

  • Erfordert öffentliche Setter für alle Eigenschaften, wodurch der Grad der Kapselung, den Sie bereitstellen können, drastisch eingeschränkt wird
  • Wenn Sie wissen, ob Sie die alte Instanz zusätzlich verwenden, müssen Sie überall wissen, dass die alte Instanz verwendet wird. Wenn also Klasse A und Klasse B beide übergebene Instanzen der Klasse C sind, müssen sie wissen, ob sie jemals dieselbe Instanz übergeben werden, und wenn ja, ob die der andere benutzt es immer noch. Dies verbindet Klassen eng, die sonst keinen Grund dazu haben.
  • Führt zu wiederholtem Code. Wie gbjbaanb angibt, besteht überall, wo der Konstruktor aufgerufen wird, keine Gefahr, einen Punkt zu verpassen, wenn Sie dem Konstruktor einen Parameter hinzufügen. Wenn Sie nur eine öffentliche Eigenschaft hinzufügen, müssen Sie sicherstellen, dass Sie jeden Ort, an dem Objekte "zurückgesetzt" werden, manuell finden und aktualisieren.
  • Erhöht die Komplexität. Stellen Sie sich vor, Sie erstellen und verwenden eine Instanz der Klasse in einer Schleife. Wenn Sie Ihre Methode verwenden, müssen Sie jetzt beim ersten Durchlaufen der Schleife oder vor dem Start der Schleife eine separate Initialisierung durchführen. In beiden Fällen müssen Sie zusätzlichen Code schreiben, um diese beiden Initialisierungsmethoden zu unterstützen.
  • Bedeutet, dass Ihre Klasse sich nicht vor einem ungültigen Zustand schützen kann. Stellen Sie sich vor, Sie haben eine Fraction -Klasse mit einem Zähler und einem Nenner geschrieben und wollten erzwingen, dass sie immer reduziert wurde (d. H. Der gcd von Zähler und Nenner war 1). Dies ist unmöglich, wenn Sie zulassen möchten, dass Personen den Zähler und den Nenner öffentlich festlegen, da sie möglicherweise durch einen ungültigen Zustand wechseln, um von einem gültigen Zustand in einen anderen zu gelangen. Z.B. 1/2 (gültig) -> 2/2 (ungültig) -> 2/3 (gültig).
  • Ist für die Sprache, in der Sie arbeiten, überhaupt nicht idiomatisch, was die kognitive Reibung für jeden erhöht, der den Code verwaltet.

Dies sind alles ziemlich bedeutende Probleme. Und was Sie als Gegenleistung für die zusätzliche Arbeit erhalten, die Sie erstellen, ist ... nichts. Das Erstellen von Instanzen von Objekten ist im Allgemeinen unglaublich billig, sodass der Leistungsvorteil fast immer völlig vernachlässigbar ist.

Wie in der anderen Antwort erwähnt, kann die Leistung nur dann von Bedeutung sein, wenn Ihre Klasse einige sehr teure Bauarbeiten ausführt. Aber selbst in diesem Fall müssen Sie in der Lage sein, den teuren Teil von den Eigenschaften zu trennen, die Sie zurücksetzen, damit diese Technik funktioniert, damit Sie das Fliegengewichtsmuster verwenden können oder ähnlich stattdessen.


Als Randnotiz könnten einige der oben genannten Probleme etwas gemildert werden, indem keine Setter verwendet werden und stattdessen ein public Reset Methode für Ihre Klasse, die dieselben Parameter wie der Konstruktor verwendet. Wenn Sie aus irgendeinem Grund diesen Rücksetzweg einschlagen wollten, wäre dies wahrscheinlich eine viel bessere Möglichkeit.

Dennoch ist die zusätzliche Komplexität und Wiederholung, die zusammen mit den oben genannten Punkten hinzugefügt wird, die nicht angesprochen werden, immer noch ein sehr überzeugendes Argument dagegen, insbesondere wenn man die nicht vorhandenen Vorteile abwägt.

16
Ben Aaronson

Angesichts des sehr allgemeinen Beispiels ist es schwer zu sagen. Wenn "Zurücksetzen der Eigenschaften" im Fall der Domäne semantisch sinnvoll ist, ist es für den Verbraucher Ihrer Klasse sinnvoller, sie aufzurufen

MyObject.Reset(); // Sets all necessary properties to null

Als

MyObject = new MyClass();

Ich würde NIEMALS verlangen, dass der Verbraucher Ihrer Klasse anruft

MyObject.Prop1 = null;
MyObject.Prop2 = null; // and so on

Wenn die Klasse etwas darstellt, das zurückgesetzt werden kann, sollte sie diese Funktionalität über eine Reset() -Methode verfügbar machen, anstatt sich darauf zu verlassen, den Konstruktor aufzurufen oder seine Eigenschaften manuell festzulegen.

9
Harrison Paine

Wie Harrison Paine und Brandin vorschlagen, würde ich dasselbe Objekt erneut verwenden und die Initialisierung der Eigenschaften in einer Reset-Methode faktorisieren:

public class MyClass
{
    public MyClass() { this.Reset() }

    public void Reset() {
        this.Prop1 = whatever
        this.Prop2 = you name it
        this.Prop3 = oh yeah
    }

    public object Prop1 { get; set; }

    public object Prop2 { get; set; }

    public object Prop3 { get; set; }
}
5
Antoine Trouve

Wenn das beabsichtigte Verwendungsmuster für eine Klasse darin besteht, dass ein einzelner Eigentümer einen Verweis auf jede Instanz behält, speichert kein anderer Code Kopien der Verweise, und es kommt sehr häufig vor, dass Eigentümer Schleifen haben, die viele Male, "füllen" Sie eine leere Instanz aus, verwenden Sie sie vorübergehend und benötigen Sie sie nie wieder (eine gemeinsame Klasse, die ein solches Kriterium erfüllt, wäre StringBuilder), dann kann sie unter Leistungsgesichtspunkten nützlich sein für Die Klasse, die eine Methode zum Zurücksetzen einer Instanz auf eine neuwertige Bedingung enthält. Eine solche Optimierung ist wahrscheinlich nicht viel wert, wenn für die Alternative nur einige hundert Instanzen erstellt werden müssten, aber die Kosten für die Erstellung von Millionen oder Milliarden von Objektinstanzen können sich summieren.

In einem verwandten Hinweis gibt es einige Muster, die für Methoden verwendet werden können, die Daten in einem Objekt zurückgeben müssen:

  1. Methode erstellt neues Objekt; gibt die Referenz zurück.

  2. Die Methode akzeptiert einen Verweis auf ein veränderbares Objekt und füllt ihn aus.

  3. Die Methode akzeptiert eine Referenzvariable als ref -Parameter und verwendet entweder das vorhandene Objekt, falls geeignet, oder ändert die Variable, um ein neues Objekt zu identifizieren.

Der erste Ansatz ist oft semantisch am einfachsten. Die zweite ist auf der anrufenden Seite etwas umständlich, bietet jedoch möglicherweise eine bessere Leistung, wenn ein Anrufer häufig in der Lage ist, ein Objekt zu erstellen und es tausende Male zu verwenden. Der dritte Ansatz ist semantisch etwas umständlich, kann jedoch hilfreich sein, wenn eine Methode Daten in einem Array zurückgeben muss und der Aufrufer die erforderliche Arraygröße nicht kennt. Wenn der aufrufende Code die einzige Referenz auf das Array enthält, entspricht das Umschreiben dieser Referenz mit einer Referenz auf ein größeres Array semantisch der einfachen Vergrößerung des Arrays (was semantisch das gewünschte Verhalten ist). Während dem Benutzen List<T> ist in vielen Fällen möglicherweise besser als die Verwendung eines Arrays mit manueller Größe. Arrays von Strukturen bieten eine bessere Semantik und Leistung als Listen von Strukturen.

2
supercat

Ich denke, den meisten Menschen, die auf die Erstellung eines neuen Objekts antworten, fehlt ein kritisches Szenario: die Garbage Collection (GC). GC kann in Anwendungen, in denen viele Objekte erstellt werden (z. B. Spiele oder wissenschaftliche Anwendungen), einen echten Leistungseinbruch erzielen.

Angenommen, ich habe einen Ausdrucksbaum, der einen mathematischen Ausdruck darstellt, wobei in den inneren Knoten Funktionsknoten (ADD, SUB, MUL, DIV) und in den Blattknoten Endknoten (X, e, PI) sind. Wenn meine Knotenklasse über eine Evaluate () -Methode verfügt, werden die untergeordneten Elemente wahrscheinlich rekursiv aufgerufen und Daten über einen Datenknoten erfasst. Zumindest müssen alle Blattknoten ein Datenobjekt erstellen, und diese können dann auf dem Weg nach oben des Baums wiederverwendet werden, bis der endgültige Wert ausgewertet ist.

Nehmen wir jetzt an, ich habe Tausende dieser Bäume und bewerte sie in einer Schleife. Alle diese Datenobjekte lösen einen GC aus und verursachen einen großen Leistungseinbruch (bis zu 40% CPU-Auslastungsverlust bei einigen Läufen in meiner App - ich habe einen Profiler ausgeführt, um die Daten abzurufen).

Eine mögliche Lösung? Verwenden Sie diese Datenobjekte erneut und rufen Sie einfach .Reset () auf, nachdem Sie sie verwendet haben. Die Blattknoten rufen nicht mehr 'new Data ()' auf, sondern eine Factory-Methode, die den Lebenszyklus des Objekts behandelt.

Ich weiß das, weil ich eine Anwendung habe, die dieses Problem behoben hat.

1