2017-01-13 6 views
1

Angenommen, wir haben eine riesige Liste von numerischen kartesischen Koordinaten (5; 3) (1; -9) usw. Um einen Punkt in oop darzustellen, habe ich eine Struktur/Objekt (C#) erstellt:Validieren Sie Objekt/Struktur ohne Fehler

public struct Point 
{ 
    public int X, Y { get; } 

    public Point(int x, int y) 
    { 
     // Check if x,y falls within certain boundaries (ex. -1000, 1000) 
    } 
} 

Es könnte falsch sein, wie ich struct verwende. Ich nehme an, normalerweise würden Sie keinen Konstruktor verwenden, aber das ist nicht der Punkt.

Angenommen, ich möchte eine Liste von 1000 Punkten hinzufügen, und es gibt keine Garantie, dass diese Koordinaten in Grenzen fallen. Wenn der Punkt nicht gültig ist, gehen Sie einfach zum nächsten ohne Fehler und informieren Sie den Benutzer darüber. Was das Objekt betrifft, würde ich denken, dass Point für die Instanziierung und Validierung selbst verantwortlich sein sollte, aber ich bin mir nicht sicher, wie ich in diesem speziellen Fall damit umgehen soll. Das Vornehmen von x, y im Voraus durch den Aufrufer wäre der einfachste Ansatz, aber es fühlt sich nicht richtig an, da der Aufrufer mit der Logik umgehen müsste, die sich in Point befinden sollte.

Was wäre der am besten geeignete Ansatz, um falsche Koordinaten zu validieren und zu behandeln, ohne SRP zu versagen und zu verletzen?

+0

[System.Drawing.Point] (https://msdn.microsoft.com/en-us/ library/system.drawing.point (v = vs.110) .aspx), muss das Rad nicht neu erfunden werden. – TheLethalCoder

+0

@TheLethalCoder 'System.Drawing.Point' ist eine veränderbare Struktur. Pfui! Es sollte aufgehängt werden, um so tief wie möglich zu trocknen und zu begraben;). Persönlich vermeide ich 'Point', es sei denn, die API, mit der ich interagiere, zwingt mich dazu. – InBetween

+0

@InBetween Habe es einfach als alternative Option gepostet, anstatt es noch einmal neu zu schreiben. Aber Ihr Punkt ist, warum ich es kommentiert und nicht als Antwort geschrieben – TheLethalCoder

Antwort

4

Sie können dies nicht im Konstruktor tun; Der Konstruktor wird entweder erfolgreich ausgeführt oder nicht. Wenn dies nicht der Fall ist, weil eine Ausnahme ausgelöst wird, also so viel, um stillschweigend zu versagen. Sie könnte Ausnahmen fangen, aber das bedeutet im Grunde genommen, dass Sie Ausnahmen als Kontrollflussmechanismus verwenden, und das ist ein großes Nein, tun Sie das nicht!

Eine Lösung ähnlich dem, was Sie denken, ist eine statische Factory-Methode zu verwenden:

public struct Point 
{ 
    public static bool TryCreatePoint(int x, int y, Bounds bounds, out Point point) 
    { 
     if (x and y are inside bounds) 
     { 
      point = new Point(x, y); 
      return true; 
     } 

     point = default(Point); 
     return false; 
    } 

    //... 
} 

Und die Codepunkte in die Liste bei der Erstellung Erfolg basiert handeln sollte.

Fun Tatsache: Wenn Sie C# 7 der Code verwenden könnte viel sauberer aussehen:

public static (bool Succesful, Point NewPoint) TryCreatePoint(int x, int y, Bounds bounds) 
{ 
    if (x and y are inside bounds) 
     return (true, new Point(x, y)); 

    return (false, default(Point)); 
} 
+0

Nette Verwendung von C# 7-Funktionen. – HimBromBeere

+0

@HimBromBeere Ich liebe die neuen Funktionen in C# 7. Wenn Sie jemals in funktionale Sprachen getappt haben, wird Mustererkennung und native Tuple-Unterstützung großartig sein. – InBetween

+0

Nur aus Neugier: Wie nennt man diese Methode? – HimBromBeere

1

Was Sie tun möchten, ist einfach nicht möglich, eine Instanz einer Klasse ist entweder vollständig erstellt oder gar nicht. Wenn der Konstruktor der einzige Weg zu nicht Instanziieren einer Instanz aufgerufen wurde, ist eine Ausnahme zu werfen.

So haben Sie diese zwei Möglichkeiten, dies zu tun:

  1. Extrahieren Sie eine Methode, die ein Validate Bool zurückgibt und kann vom Anrufer Ihrer Klasse aufgerufen werden.

    public struct Point 
    { 
        public int X, Y { get; } 
    
        public Point(int x, int y) 
        { 
        } 
    } 
    public bool Validate() { return -1000 <= X && X <= 1000 && -1000 <= Y and Y <= 1000; } 
    

    Natürlich könnten Sie das gleiche mit einer Eigenschaft tun.

  2. werfen eine Ausnahme im Konstruktor

    public Point(int x, int y) 
    { 
        if(x > 1000) throw new ArgumentException("Value must be smaller 1000"); 
        // ... 
    } 
    

jedoch die beste Lösung IMHO ist die Eingabe zu bestätigen, bevor Sie selbst über das Erstellen von einen Punkt denken, dass die Argumente an den Konstruktor übergeben vorher überprüfen :

if(...) 
    p = new Point(x, y); 
else 
    ... 
1

ich denke an drei Optionen können:

  1. Lassen Sie den Konstruktor eine Ausnahme auslösen, die Sie fangen. Das ist nicht wirklich toll, wenn Sie viele Fehler erwarten.

  2. Haben Sie eine IsValid -Eigenschaft in der Struktur, die Sie verwenden können, um sie nach der Erstellung auszufiltern.

  3. Lassen Sie die Daten beim Laden der Daten auch für die Validierung der Daten übernehmen. Dies wäre meine bevorzugte Option. Sie sagen "es fühlt sich nicht richtig an, da der Aufrufer mit der Logik umgehen müsste, die in Point enthalten sein sollte", aber ich würde argumentieren, dass die Verantwortung für die Überprüfung der geladenen Daten darin besteht, die Daten zu laden, nicht den Datentyp. Sie könnten auch haben werfen sie eine ArgumentOutOfRangeException im Konstruktor, wenn die Eingaben nicht gültig sind, jetzt da Sie nicht mehr erwarten, dass etwas Ungültiges als ein Gürtel und Armschienen Ansatz an Dinge weitergegeben werden.

0

Um ehrlich zu sein, Punkt sollte nicht Grenzen überprüfen, so der Anrufer sollte das tun. Ein Punkt ist in dem Bereich gültig, in dem X und Y arbeiten können (int.MinValue und int.MaxValue). Also ein -1000000,2000000 ist ein gültiger Punkt. Das Problem ist, dass dieser Punkt nicht für Ihre Anwendung gültig ist, daher sollte Ihre Anwendung (der Aufrufer), derjenige, der einen Punkt verwendet, diese Logik haben, nicht innerhalb des Punktkonstruktors.

+1

IMHO. Nun, Point ist ein benutzerdefiniertes Objekt, das darstellen soll, was ich will. Ich kann sagen, dass (5000; 5000) kein Punkt ist, weil er nicht den Regeln entspricht. Ich würde die technischen Grenzen eines int mit Geschäftsregeln nicht mischen. – DasBoot

+2

Ja, das stimmt, aber ein Punkt ist ein Punkt, und (5000; 5000) ist immer noch ein gültiger Punkt. Ihre Regeln sind Geschäftsregeln, und das sollte sich für mich nicht auf den Point auswirken (als generische Klasse). Was würden Sie tun, wenn Sie die gleiche Klasse (in diesem Fall Point) in einem anderen Projekt wiederverwenden möchten, in dem die Grenzen unterschiedlich sind? –

+0

Es hängt wirklich von bestimmten Domain-Regeln ab. Wenn Sie eine klare Aussage haben, dass ein Punkt auf keinen Fall mehr als ein bestimmter Wert sein kann, dann ist es das. Es bedeutet, dass es keinen solchen Fall gibt wie was, wenn dies oder das (und das ist mein Fall). In einem anderen Fall würde ich Ihnen vollkommen zustimmen und es könnte durch die Einführung eines Untertyps für Point (ex. LimitedPoint) geklärt werden. – DasBoot

0

Structs in C# sind lustig, damit ich eine andere "lustig" Art und Weise hinzufügen würde überprüfen:

struct Point 
{ 
    int _x; 
    public int X 
    { 
     get { return _x; } 
     set { _x = value; ForceValidate(); } 
    } // simple getter & setter for X 

    int _y; 
    public int Y 
    { 
     get { return _y; } 
     set { _y = value; ForceValidate(); } 
    } // simple getter & setter for Y 

    void ForceValidate() 
    { 
     const MAX = 1000; 
     const MIN = -1000; 

     if(this.X >= MIN && this.X <= MAX && this.Y >= MIN && this.Y <= MAX) 
     { 
      return; 
     } 
     this = default(Point); // Yes you can reasign "this" in structs using C# 
    } 
} 
+0

WTF ?! Unvorstellbar, das funktioniert tatsächlich. Scheint ziemlich hässlich für mich, aber es ist seine Aufgabe. – HimBromBeere

+0

@HimBromBeere Ich weiß, es ist hässlich: D Aber es bringt mich zum Lachen, wenn ich die Zuordnung zu "this" sehe. –

+0

Du hast definitiv die +1 für dieses seltsame C# -Feature verdient (und die Jungs von MS verdienen einen Tritt in den Arsch). – HimBromBeere