Wann soll [Pure] in einem Konstruktor verwendet werden?

19

Ich lerne etwas über Codeverträge in .NET und versuche, die Idee reiner Konstruktoren zu verstehen. Der Code Verträge Dokumentation lautet:

Alle Methoden, die in einem Vertrag aufgerufen werden, müssen rein sein. Das heißt, sie dürfen keinen bereits vorhandenen Status aktualisieren. Eine reine Methode darf Objekte ändern, die nach dem Eintritt in die reine Methode erstellt wurden.

Und die PureAttributeDokumentation besagt:

Gibt an, dass ein Typ oder eine Methode rein ist, dh, dass keine sichtbaren Statusänderungen vorgenommen werden.

Ich verstehe diese Aussagen, wenn es um Methoden geht, aber was ist mit Konstruktoren? Angenommen, Sie hatten eine Klasse wie diese:

public class Foo
{
    public int Value { get; set; }

    public Foo(int value) {
        this.Value = value;
    }
}

Dieser Konstruktor wirkt sich offensichtlich auf den Zustand des neuen FooObjekts aus, hat jedoch keine weiteren Nebenwirkungen (z. B. er manipuliert keinen der Parameter oder ruft keine nicht-reinen Methoden auf). Ist das ein Kandidat für [Pure]oder nicht? Welche Bedeutung hat das Platzieren eines [Pure]Attributs in einem Konstruktor, und wann sollte ich dies in meinem eigenen Code tun?

pswg
quelle

Antworten:

14

Du dekorierst eine Methode mit [Pure]:

  • Wenn die Methode keine Nebenwirkungen hat. Wenn die Methode beispielsweise auf eine Datenbank zugreift und diese ändert oder ihr Ergebnis von der Datenbank abhängt, ist sie nicht rein.

  • Und wenn Sie damit rechnen, es in Codeverträgen zu verwenden. Wenn die Methode beispielsweise rein ist, Sie sie jedoch nicht in Codeverträgen verwenden [Pure]möchten , hat das Hinzufügen keinen Vorteil und beschleunigt den Code nicht.

In Bezug auf Konstruktoren wird davon ausgegangen , dass sie in .NET rein sind und kein explizites Attribut benötigen. Ich habe mir mehrere Konstruktoren in .NET Framework angesehen, z. B. DateTime, und sie haben keine [Pure]Attribute.

Ich nehme an, dies geschieht aus mehreren Gründen:

  • Es kann zu unpraktisch sein, einen parameterlosen Konstruktor mit [Pure]Attribut schreiben zu müssen, um die Klasse / Struktur in einem Vertrag verwenden zu können.

  • Einige, wie zum Beispiel String, haben keine expliziten Konstruktoren.

  • Die Konstrukteure erhalten auch außerhalb von Code-Verträgen eine Sonderbehandlung; Es wird beispielsweise nicht erwartet , dass Sie Ausnahmen in sie werfen .

  • [Pure]Dies ist nur eine Konvention, die Ihr Leben vereinfachen soll. Es gibt jedoch keine statische Überprüfung, um sicherzustellen, dass die mit diesem Attribut dekorierte Methode rein ist. void DestroyDatabase()kann als pur dekoriert werden und Code-Verträge werden nichts falsches bemerken.

    Derzeit gibt es keine Komponente in Code Contracts, die prüft, ob als rein deklarierte Methoden tatsächlich rein sind. Wenn also ein Programmierer eine Methode mit [Pure] dekoriert, wird nur geglaubt.

    Aus den Code-Verträgen Nr. 5: Methodenreinheit

  • .NET Framework selbst enthält Konstruktoren, die nicht rein sind. List<T>(IEnumerable<T> collection)Ist beispielsweise tatsächlich unrein, wenn das Durchlaufen der Sammlung Nebenwirkungen hat.

  • Verträge sollten einfach gehalten werden. Ich kann mir so einen Vertrag gut vorstellen Contract.Requires(!string.IsNullOrEmpty(name)), also gibt es gute Gründe die Statik string.IsNullOrEmptyrein zu deklarieren .

    Wenn Sie andererseits StringBuilderzum Erstellen der Zeichenfolge eine benötigen, überprüfen Sie, ob etwas vorhanden ist, indem Sie eine Instanzmethode Ihrer Business-Klasse aufrufen, und missbrauchen wahrscheinlich die Verträge. Das ist auch der GrundStringBuilder.ToString nicht als rein markiert ist, auch wenn es sein könnte (oder?)

Arseni Mourzenko
quelle
Viele Codeverträge für Systemtypen werden von der Vertragsprüfung übernommen, einschließlich " jeder Methode, deren vollständig qualifizierter Name mit" System.Diagnostics.Contracts.Contract "," System.String "," System.IO.Path "oder" System "beginnt .Type " ". Leider bin ich mir nicht sicher, ob ein Blick auf die .NET-Typen zu nützlich ist, wenn es um Code-Verträge geht.
Pswg
3
Reinheit ist eines jener Dinge, bei denen jeder aufgerufene Code auch rein sein muss oder der Aufrufer nicht "rein" ist. Ich finde es schwierig zu glauben, dass alle Konstruktoren standardmäßig als rein betrachtet werden.
Frank Hileman
@FrankHileman: Ich auch. Ich habe derzeit keinen C # -Compiler, aber es würde ausreichen, eine Klasse mit einem Konstruktor und keinem [Pure]Attribut zu schreiben und sie an einer anderen Stelle in einem Vertrag zu verwenden, um eine endgültige Antwort zu erhalten.
Arseni Mourzenko
1

Das Objekt kann erst verwendet werden, wenn es in diesem Fall erstellt wurde. Daher ist der Konstruktor rein. Wenn der Konstruktor einen anderen Code aufruft oder einen Delegaten aufruft und der andere Code die veränderbare Eigenschaft ändert, ist sie nicht rein. Um sicherer zu sein, ist es besser, die Immobilie unveränderlich zu machen.

Frank Hileman
quelle
Ein reiner Konstruktor ist also eine reine Methode, die den Zustand der aktuellen Klasse ändern darf, solange sie die anderen Bedingungen erfüllt, um rein zu sein. Übrigens, die Eigenschaft ist veränderlich, weil ich betonen wollte, dass diese Klasse selbst nicht rein ist.
Pswg
@pswg: Sie haben eine interessante Frage erstellt, die wahrscheinlich von Microsoft beantwortet werden muss. Angenommen, der Konstruktor hat eine Methode aufgerufen, mit der die veränderbare Eigenschaft geändert wurde. Wäre der Konstruktor noch rein? Ich denke technisch wäre es nicht, obwohl die Modifikation für externe Betrachter "unsichtbar" ist. Da in Ihrem ursprünglichen Beispiel kein anderer Code aufgerufen wird, muss er nach jeder Definition, die mir einfällt, rein sein.
Frank Hileman
@pswg: Außer, dass der Eigenschaftensatz auch ein Methodenaufruf ist. Ich denke, Sie sollten in den MSDN-Foren nachfragen.
Frank Hileman
Ich denke, wenn die zentrale Idee der Reinheit ist, ob die Methode beobachtbare Änderungen vornimmt oder nicht , dann in diesem Sinne, unabhängig davon, ob sie nicht-reine Methoden aufruft oder nicht, solange die Änderungen von keinem Aufrufer beobachtet werden können. Es wäre immer noch eine reine Methode.
Pswg
@pswg: Das ist die abstrakte Definition. Wenn ich jedoch einen Analysator für diese Dinge schreibe, wird wahrscheinlich auch ein nicht reiner Methodenaufruf in Betracht gezogen, um den Aufrufer nicht rein zu machen. Nur zur Vereinfachung der Implementierung. Dann ist die Frage, ob ein Konstruktor ein normaler Methodenaufruf ist oder wie tief die Analyse geht.
Frank Hileman