2016-08-23 2 views
14

SITUATIONDelphi raise Ausnahme in Konstruktor

Ich werde eine Klasse schreiben und der Konstruktor ist ein Brauch, die ich gemacht habe, weil ich einige Werte initialisieren müssen. Dies ist der Code, den ich bisher geschrieben habe:

type 
TCombinatorio = class(TObject) 
    private 
    valN, valK: integer; 
    result: double; 
    public 
    property K: integer read valK; 
    property N: integer read valN; 
    constructor Create(valN: integer; valK: integer); 
end; 

constructor TCombinatorio.Create(valN: Integer; valK: Integer); 
begin 
    inherited Create; 
    Self.valN := valN; 
    Self.valK := valK; 

    if ((valN < 0) or (valK < 0)) then 
    begin 
    raise Exception.Create('N and K must be >= 0'); 
    end; 

end; 

Da ich einige mathematische Berechnungen werde tun, ich brauche negative Zahlen zu vermeiden.


FRAGE

Kann ich eine Ausnahme im Konstruktor auf diese Weise nennen? Ich verwende den Code auf diese Weise:

procedure TForm1.Button1Click(Sender: TObject); 
var a: TCombinatorio; 
    b: string; 
begin 

a := TCombinatorio.Create(5,-2); 

try 
    //some code 
finally 
    a.Free; 
end; 

end; 

Wie Sie hier sehen, ich habe falsche Parameter für meinen Konstruktor, da die zweiten negativ ist. Ich kann auch nicht verstehen (nach dem Code meines Konstruktors), wenn die a.Free innerhalb der endlich wirklich benötigt wird, denn wenn der Konstruktor die Ausnahme auslöst, wird der Destruktor aufgerufen.

Ich dachte, die a := TCombinatorio.Create(5,-2); in den try-finally Block zu integrieren, um das Problem zu vermeiden, aber ich bin mir nicht sicher. Was denken Sie?

Antwort

16

Ihr Code ist absolut in Ordnung und korrekt. Das Aufheben von Ausnahmen von Konstruktoren ist absolut respektabel. Wie Sie wissen, wird der Destruktor aufgerufen.

Sie fragen nach diesem Code:

a := TCombinatorio.Create(5,-2); 
try 
    //some code 
finally 
    a.Free; 
end; 

Sie sind besorgt, dass Free aufgerufen wird, nachdem das Objekt bereits zerstört worden. Das kann nicht passieren. Wenn eine Exception im Konstruktor ausgelöst wird, wird der Aufruf-Stack weitergeleitet. Das passiert, bevor der try-Block beginnt und daher der finally-Block nicht ausgeführt wird. Tatsächlich erfolgt die Zuordnung zu a nicht.

Das Verschieben der Schöpfung innerhalb der try wäre katastrophal und ist in der Tat ein unglaublich häufiger Fehler. Angenommen, Sie haben das:

// WARNING THIS CODE IS DEFECTIVE 
try 
    a := TCombinatorio.Create(5,-2); 
    //some code 
finally 
    a.Free; 
end; 

Nun, wenn eine Ausnahme ausgelöst wird, dann wird Free genannt, aber auf was? Die Variable a ist nicht initialisiert. Selbst wenn es das wäre, was es nicht ist, wäre es immer noch doppelt frei.

+0

Nun, da ich vorsichtig gerade lese, denke ich, dass ich ein gemachter dumme Überlegung. Im Falle einer Ausnahme, die im Konstruktor ausgelöst wird, wird try-finally nicht ausgeführt, weil ich vorher stoppe. Also muss ich mir keine Sorgen machen und ich werde die Kreation nicht in den Block aufnehmen. Sehr hilfreich;) –

+0

Das Erstellen von Ausnahmen in Konstruktoren in Delphi ist Antipattern und sollte normalerweise nie verwendet werden. – kludg

+2

@kludg - Wenn Sie zur Konstruktionszeit wissen, dass eine Instanz unbrauchbar sein wird, ist der Konstruktor der beste Ort, um ein Exception-Imo auszulösen. Die Alternative besteht darin, eine Ausnahme auszulösen, wenn die Instanz verwendet wird, was zu interessanten Debugging-Sitzungen führen könnte, um zu wissen, woher der Fehler stammt. Das heißt, in diesem speziellen Fall sollte OP zu vorzeichenlosen Ganzzahlen wechseln. –

3

OK, zuerst können Sie eine Exception im Konstruktor auslösen, und ja, es ruft den Destruktor als Konsequenz auf. Der Code, den du zeigst, ist in Ordnung. Aber ich glaube, du verstehst falsch, was dein Code tut. Und um den Konstruktor in einen Versuch zu setzen, wäre endlich der Block falsch. Der Punkt, den ich denke, dass Sie vermissen, ist, dass, wenn Ihr Konstruktor fehlschlägt, der try...finally Block nie ausgeführt wird und so das freie nicht ausgeführt wird. Sie sollten nicht kostenlos aufrufen, wenn der Konstruktor nicht erfolgreich ist, deshalb sollten Sie nicht den Konstruktor in den try...finally Block setzen.

1

Zunächst würde ich sagen, dass Sie Ausnahmen in Konstruktoren nicht vermeiden können, so dass es kein Anti-Muster sein kann. Wenn Sie den Delphi-Quellcode überprüfen, finden Sie die Anzahl der Stellen, an denen die Exception im Konstruktor ausgelöst wird.Zum Beispiel

constructor TCustomForm.Create(AOwner: TComponent); 
begin 
    // ... skipped some lines 
     if not InitInheritedComponent(Self, TForm) then 
      raise EResNotFound.CreateFmt(SResNotFound, [ClassName]); 

Das einzige, was Sie wissen sollten ist, dass Delphi wird die destructor automatisch anrufen, wenn eine Ausnahme von dem Konstruktor entweicht. Tatsächlich bedeutet dies, dass Ihr Destruktor auf einem teilweise konstruierten Objekt ausgeführt werden kann und es in Ihrer Verantwortung liegt, destructor ordnungsgemäß zu schreiben. Siehe TObject.Destroy Dokumentation und bezahlen Sie Ihre besondere Aufmerksamkeit auf das folgende Zitat:

Hinweis: Wenn eine Ausnahme von dem Konstruktor entkommt, wird der Destruktor aufgerufen, die teilweise konstruierten Objektinstanz zu zerstören, dass vollständig konnten nicht initialisiert werden. Daher sollten Destruktoren überprüfen, dass zugewiesene Ressourcen wie z. B. Handles tatsächlich zugewiesen wurden, bevor Sie versuchen, sie zu veröffentlichen, da ihr Wert möglicherweise Null ist.

PS Generell sollte man, dass jede Codezeile annehmen kann eine Ausnahme auslösen, aber bitte ein Paranoiker nicht sein;)

+0

Ich würde hinzufügen, dass wenn Sie dies tun (Exceptions in Ctor auslösen), müssen Sie testen, ob Ihr Objekt erstellt wird oder nicht vor jedem Aufruf in Ihrer Kundenanwendung! Denn wenn eine Ausnahme auftritt -> destructor wird automatisch aufgerufen -> object = nil -> 'object.DoSomething' führt zu Acces Violation – paradise

+0

Normalerweise ist es nicht notwendig, es zu testen, weil Ihr' object.DoSomething' Code nicht ausgeführt werden sollte wegen der Ausnahme. Ich würde mich freuen, wenn Sie ein Code-Beispiel veröffentlichen könnten, wo diese Überprüfung erforderlich ist. Ich nehme an, dass Sie entweder die Konstruktorausnahme manuell behandelt haben oder Ihr Code vom Destruktor aus aufgerufen werden kann. –