![]() |
Guter Stil oder 'Wie programmiere ich vernünftig?'
Hallo,
leider ist mir kein besserer Threadtitel eingefallen. In letzter Zeit bin ich beim Programmieren immer wieder auf Konstrukte gestoßen, bei denen ich mir unsicher bin, wie ich sie in sauberen Quellcode presse. Natürlich habe ich mir dazu Gedanken gemacht und werde diese hier darstellen. Ich bitte euch, meine Gedanken zu kommentieren und mir ggf. einen besseren Weg vorzuschlagen. Ich verwende in meinen Beispielen hier aufgrund meines aktuellen Projektes .Net-Code. Die Probleme lassen sich aber ebenso in Win32-Code darstellen. Fall 1: Ich führe eine Prozedur aus, die auf verschiedene Werte aus einer Ini-Datei angewiesen ist. Fehlt einer der Werte, kann die Aufgabe nicht erfüllt werden. Es soll dann eine Fehlermeldung ausgegeben und die Verarbeitung abgebrochen werden. Prinzipiell läßt sich dieses Problem auf jeden Programmteil verallgemeinern, der linear abgearbeitet und im Fehlerfall unterbrochen werden soll. Lösungsidee: Einlesen aller Werte nacheinander. Falls ein Wert fehlt -> Ausgabe einer Meldung und Prozedurabbruch per exit Beispiel:
Delphi-Quellcode:
Kann man das so machen?
If not ReportDoc.GetLogOnData(ReportDoc.GetIniValue('AutoDB'), loServer,
loDB, loUser, loPass) then begin MessageBox.Show('Es konnten keine Logindaten für die Datenbank ' + 'gefunden werden.' + #13#10 + 'Ini-Datei, Key: AutoDB', 'Fehler', MessageboxButtons.OK, MessageBoxIcon.Error); exit end; If Directory.Exists(OutputPath) then begin MessageBox.Show('Das Ausgabeverzeichnis existiert bereits.', 'Fehler', MessageboxButtons.OK, MessageBoxIcon.Error); exit end else try Directory.CreateDirectory(OutputPath) except MessageBox.Show('Fehler beim Erstellen des Ausgabeverzeichnisses.' + Environment.NewLine + '(' + OutputPath + ')', 'Fehler', MessageboxButtons.OK, MessageBoxIcon.Error); exit end; Ein Problem fällt mir dabei sofort auf. Diesen handle ich mir durch das exit ein. Falls ich vor diesem Abschnitt z.B. einen Button deaktiviere und danach wieder aktivieren will, wird dies im Fehlerfall nicht ausgeführt, da die Prozedur einfach abgebrochen wird. Fall 2: Es ist ja hinlänglich bekannt, dass man zum Ressourcenschutz try..finally..end-Blöcke verwendet. Was mache ich aber, wenn ich gleich mehrere Objekte zur Laufzeit erstellen muß? Es kann ja prinzipiell überall ein Fehler auftreten. Das einzige, was mir einfällt, wäre ein Schachteln der Blöcke. Hier ein Beispiel:
Delphi-Quellcode:
Also wenn das nich grausam aussieht... Geht sowas nicht hübscher?
SqlConn := SqlConnection.Create;
try SqlConn.ConnectionString := 'Application Name=' + self.Text + ';' + 'Persist Security Info=True;' + 'Data Source=' + loServer + ';' + 'Initial Catalog=' + loDB + ';' + 'User Id=' + loUser + ';' + 'Password=' + loPass + ';'; SqlComm := SqlCommand.Create(SqlAbfrage, SqlConn); try LogLines := StringCollection.Create; try try SqlConn.Open; except MessageBox.Show('Fehler bei der Anmeldung an der Datenbank.', 'Fehler', MessageboxButtons.OK, MessageBoxIcon.Error); exit end; try SqlDR := SqlComm.ExecuteReader; except // Fehlermeldung ausgeben... end; try // Daten hier verarbeiten finally SqlDR.Free end; finally LogLines.Free end; finally SQLComm.Free end:; finally SQLConn.Free end; Das waren jetzt erstmal die 2 Fälle, die mir besonders auf dem Herzen lagen. Ich hoffe, ihr könnt mir weiterhelfen, damit sich mein Programmierstil wieder ein Stück verbessern kann. Falls mir noch etwas einfällt, weiß ich ja, wo ich Hilfe bekomme. :wink: Danke für eure Hilfe, Stephan |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Zum ersten Fall:
Ich werfe in so einem Fall eine Exception, dich ich dann im Aufrufer abfange. Beispielsweise habe ich eine Klasse, in dieser Klasse wird einer Eigenschaft eine Datei zugewiesen, existiert diese Datei nicht, werfe ich eine Exception, da ohne diese Datei alles weitere sinnlos ist. Somit bricht die Klasse selber alles weiter ab und ich mussmich nur noch darum kümmern die Exception zu behandeln. Zum zweiten Fall: Brauche ich meherer Objekte, erstelle ich alle Objekte gleich am Anfang und gebe sie auch alle im gleichen finally-Block wieder frei. |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Zitat:
Zitat:
|
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Hallo,
Zitat:
Eingene Exception-Typen wärend da sauberer wegen einer möglichen Behandlung durch:
Delphi-Quellcode:
Richtig?
except
on ... do on ... do Zitat:
Zitat:
Dazu fällt mir noch eine Frage ein: Wenn ich mir z.B. im MSDN .NET-Beispielcode anschaue (meist in C#), werden da ja eigentlich nie Destruktoren aufgerufen. Also sowas wiw Free bzw. FreeAndNil. Bei Forms und einigen wenigen Sachen gibts zwar ein Dispose, aber auch das wird nie explizit aufgerufen. Lassen die alles über den Garbage Collector aufräumen? Sind wir Delphianer die auberen Programmierer? Und kann ich bei Objekten, die kein Dispose anbieten den Aufruf von Free auch einfach weglassen, weil dieser sowieso nix macht? Gruß, Stephan |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Wenn Du in .Net arbeitest, brauchst Du eh nicht alle Objekte freigeben. Ein Free bringt effektiv nur etwas auf einem Objekt, dass
a) entweder Free selbst implemetiert (kommt selten vor) b) IDisposable implementiert In Delphi.Net hat allerdings jede Klasse eine Free-Methode, da hier objekt-helpers / compiler magic einschreitet. Ansonsten finde ich das Schachteln von try/finally Blöcken aber das Sauberste. Wenn man das auch nur noch bei Objekten aufruft, wo es auch nötig ist, dann wird die Verschachtelungstiefe auch nicht sooo tief (SqlCommand braucht zum Beispiel kein Free, da nicht IDisposable). Wenn eine Klasse aber IDisposable implementiert, dann sollte man das Dispose auch aufrufen. In Delphi kann man dazu Free aufrufen. In anderen .Net Sprachen wird Dipose meistens schon aufgerufen, man muss nur wissen, wo man gucken muss. Wenn man es vergisst, ist es aber meistens auch nicht ganz so schlimm, da irgendwann der Garbage Collector einschreitet. Grüße |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Zitat:
Zitat:
Mittels Interfaces kann man sich AutoPtr (C++) basteln. (muss ich mal schnell was zusammenschreiben...) |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Zitat:
|
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Ein Blick in Borland.Delphi.System bringt übrigens auch etwas Klarheit dahin, was .Free eigentlich bewirkt:
Delphi-Quellcode:
procedure TObjectHelper.Free;
var FreeNotify: IFreeNotify; begin if (Self <> nil) and (Self is IDisposable) then begin // ... snip ... (copyrighted high-tech code) (Self as IDisposable).Dispose; end; end; |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Hier mal meine schnell zusammengetippte AutoPtr Klasse:
Delphi-Quellcode:
Und hier, wie man den AutoPtr nutzt
unit AutoPtr;
interface type IAutoPtr = interface function AsPointer: Pointer; function AsObject: TObject; end; function CreateAutoPtr(Value: TObject): IAutoPtr; implementation type TAutoPtr = class(TInterfacedObject, IAutoPtr) private FValue: TObject; public constructor Create(AValue: TObject); destructor Destroy; override; function AsPointer: Pointer; function AsObject: TObject; end; function CreateAutoPtr(Value: TObject): IAutoPtr; begin Result := TAutoPtr.Create(Value); end; { TAutoPtr } constructor TAutoPtr.Create(AValue: TObject); begin inherited Create; FValue := AValue; end; destructor TAutoPtr.Destroy; begin FValue.Free; inherited Destroy; end; function TAutoPtr.AsObject: TObject; begin Result := FValue; end; function TAutoPtr.AsPointer: Pointer; begin Result := FValue; end; end.
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
var List, List2, List3: TList; begin List := CreateAutoPtr(TList.Create).AsPointer; List2 := CreateAutoPtr(TList.Create).AsPointer; List3 := CreateAutoPtr(TList.Create).AsPointer; List.Add(nil); List2.Add(nil); List3.Add(nil); end; // hier räumt Delphi dann automatisch auf |
Re: Guter Stil oder 'Wie programmiere ich vernünftig?'
Alles klar. Auf den Blick in die Borland.Delphi.System hätte ich auch kommen können. Das heißt also, wenn das Objekt IDisposable nicht implementiert, ist ein Free überflüssig. Ansonsten sollte man es aufrufen. So dachte ich mir das auch.
Ich werde mich mal an die Umstrukturierung meines Queltextes machen. Bei neuen Frage, melde ich mich bestimmt wieder. Bis dahin, Stephan |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:35 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz