2009-08-26 9 views
3

Eine Beispielklasse im "C# Class Desing Handbook" (S. 137) ruft die Klassenvalidierungsmethode für ein bestimmtes Feld nicht innerhalb des Konstruktors nur für Klassen auf. Die Beispielklasse ermöglicht es Ihnen also, ein Objekt mit ungültigen Daten zu erstellen und nur dann einen Fehler für diese Daten zu erzeugen, wenn Sie die Eigenschaft des Feldes aufrufen, die dann die Validierung durchführt. Du hast also ein schlechtes Objekt und weißt es erst nachher.Konstruktordaten validieren

Ich habe nie verstanden, warum sie nicht einfach die Eigenschaft vom Konstruktor aufrufen und sofort einen Fehler auslösen, wenn während der Initialisierung fehlerhafte Daten gefunden werden? Ich habe sie ohne Erfolg gemailt ...

Ich neige dazu, das folgende Format zu verwenden, indem ich meine Eigenschaften von meinen Konstruktoren aufrufe - ist diese richtige Struktur, um Initialisierungsdaten zu validieren?

ty
class Foo 
{ 
    private string _emailAddress; 

    public Foo(string emailAddress) 
    { 
     EmailAddress = emailAddress; 
    } 

    public string EmailAddress 
    { 
     get { return _emailAddress; } 
     set 
     { 
      if (!ValidEmail(value)) 
       throw new ArgumentException 
        (string.Format 
        ("Email address {0} is in wrong format", 
        value)); 

      _emailAddress = value; 
     } 
    } 


    private static bool ValidEmail(string emailAddress) 
    { 
     return Regex.IsMatch 
      (emailAddress, @"\b[A-Z0-9._%+-]+" + 
          @"@[A-Z0-9.-]+\.[A-Z]{2,4}\b", 
          RegexOptions.IgnoreCase); 
    } 
} 
+0

Dies sollte wahrscheinlich eine Gemeinschaftsfrage sein - könnte jemand bitte richtig einstellen, wenn ja. – user10178

Antwort

2

Nun, zum einen, sind Sie wahrscheinlich die gefürchtete Nullreferenceexception bekommen, da Sie nicht überprüft werden, ob emailaddress auf jeder Ebene null ist. Diese spezielle Überprüfung sollte im Konstruktor selbst durchgeführt werden, und wenn emailAddress NULL ist, eine ArgumentNullException werfen. Im übrigen sehe ich keine besonderen Probleme damit, wie es in Ihrer Probe steht. Es gibt jedoch einige Probleme, die auftreten können, wenn Sie die Eigenschaft virtuell machen und untergeordnete Elemente aus dieser Klasse ableiten. Die Ausführungsreihenfolge von Feldinitialisierung, Basis- und abgeleiteten Klassenkonstruktoren wird dann zu einem Problem, und Sie müssen vorsichtig sein.

+0

Ich habe es für die Klarheit weggelassen, aber ja danke ... – user10178

+0

Gern geschehen. :) – jrista

0

Ich sehe nichts falsch mit diesem Ansatz. Sie können Methoden dazu im Konstruktor aufrufen, und Property-Setter/Getter sind nur syntaktische Zucker für Methodenaufrufe.

+0

Die Klasse verhält sich anders, wenn die E-Mail-Adresse im Konstruktor vs. über den Eigenschaften-Setter festgelegt ist. Inkonsistentes Verhalten ist keine gute Designeigenschaft :-) –

+0

im Beispielcode der angegebene Asker verwendet der Konstruktor den Property Setter, soweit ich das beurteilen kann, also wäre das Verhalten in der Tat konsistent. Gibt es etwas, das ich vermisse? – Kevlar

2

Ja, wenn Ihr allgemeiner Ansatz ist:

Stellen Sie sicher, dass Sie nur eine Instanz ein valid Objekt bekommen können

dann habe ich es lieben.

Konstrukteurs sollten Objekte verwendet werden, zu erstellen, die sofort gültig sind, nicht nur einen ‚Container‘ zu schaffen, für die Dinge in gestellt werden.

+0

Ja, nur gültige Objekte zu bekommen ist die Idee hier - ich füge Überladungen des Konstruktors hinzu, um Benutzern zu erlauben, ein leeres Feld einzugeben, wo es in Ordnung ist, dies zu tun. ty. – user10178

0

die Validierung geschieht, wenn die E-Mail-Adresse eingestellt ist. Dies ist, wo Sie es wollen, weil die E-Mail-Adresse möglicherweise später wieder eingestellt werden kann.

Wenn Sie im Konstruktor auch Validierung aufgerufen haben, würden Sie einen zusätzlichen, redundanten Überprüfungsaufruf durchführen (einmal beim Erstellen, ein anderer, wenn die E-Mail-Adresse im Konstruktor festgelegt ist).

+0

Huh? Die ursprüngliche Einstellung der E-Mail-Adresse befindet sich im Konstruktor, der die Eigenschaft _emails aufruft. Wenn ich _email direkt setze, dann würde ich ein schlechtes Objekt haben, wenn eine ungültige E-Mail übergeben würde. Es würde nur validiert werden, wenn die Eigenschaft geändert wurde, nachdem das Objekt instanziiert wurde. – user10178

+0

Der Konstruktor ruft den Setter der Eigenschaft auf: "EmailAddress" – apiguy

+0

Ja, ich verstehe - ich habe es geschrieben. Was ich nicht verstanden habe, warst du Kommentar ... Entschuldigung. – user10178

2

Es macht keinen Sinn, Daten im Konstruktor nicht zu validieren. Wie Sie darauf hinweisen, kann das Objekt in einem ungültigen Zustand enden. Angesichts dieses Designs würden Sie nicht einmal bemerken, dass Sie schlechte Daten hatten, wenn Sie den Getter aufrufen.

Für etwas moderater Komplexität oder höher tendiere ich dazu, einen Broken Rules-Ansatz zu verwenden, anstatt sofort eine Exception auszulösen. In diesem Ansatz definiere ich ein BrokenRules-Objekt, das Informationen über die Klasse und Eigenschaft enthält, die ungültig ist, und den Grund, dass sie ungültig ist. Dann definiere ich in einer gemeinsamen Basisklasse eine Liste, um eine Liste von allem "Falschen" über das Objekt aufzunehmen. Eine Eigenschaft (wiederum in der Basisklasse) IsValid gibt an, ob es momentan irgendwelche gebrochenen Regeln gibt.

Der Vorteil davon ist, dass möglicherweise mehrere Dinge mit dem Objektstatus falsch sind. Wenn ein Benutzer aufgefordert wird, die Probleme zu beheben (z.Dieses Objekt wird von einer Benutzerschnittstelle aus eingestellt, wobei eine Liste aller Probleme zur Verfügung gestellt wird, die der Benutzer in einem Schritt korrigieren kann, anstatt einen Fehler zu beheben, nur um zu erfahren, dass es einen anderen gibt. Und noch einer. Etc.

+0

Danke Eric - Ich werde den Ansatz der Broken Rules untersuchen! Ich fange einfach an, Frameworks zu bauen und noch zu lernen. Aufgegriffene "Framework Design Guidelines", mit denen ich viel Spaß habe! Tolles Buch. – user10178