AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Guter Stil oder 'Wie programmiere ich vernünftig?'
Thema durchsuchen
Ansicht
Themen-Optionen

Guter Stil oder 'Wie programmiere ich vernünftig?'

Ein Thema von TPok · begonnen am 19. Apr 2006 · letzter Beitrag vom 19. Apr 2006
Antwort Antwort
Seite 1 von 2  1 2      
TPok

Registriert seit: 19. Jul 2004
Ort: Eisenach
25 Beiträge
 
Delphi 2005 Professional
 
#1

Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 00:19
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:
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;
Kann man das so machen?
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:
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;
Also wenn das nich grausam aussieht... Geht sowas nicht hübscher?

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.

Danke für eure Hilfe,
Stephan
Stephan Fietzek
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#2

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 00:34
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.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#3

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 00:35
Zitat von TPok:
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.
Du kannst dein Disable/Enable des Buttons mittels einer try/finally "schützen". Das Exit springt dann in das finally.

Zitat:
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.
Alternativ kannst du auch alle Variablen vor dem ersten try auf NIL setzen und dann in einem einzigen finally diese freigeben, ohne dich um "if variable <> nil then..." zu kümmern, weil das .Free bzw. FreeAndNil selbst abfrägt. Dabei sollte aber vom Destruktor keine Exception geworfen werden, weil sonst alle folgenden Free Aufrufe nicht mehr durchgeführt werden.
  Mit Zitat antworten Zitat
TPok

Registriert seit: 19. Jul 2004
Ort: Eisenach
25 Beiträge
 
Delphi 2005 Professional
 
#4

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 00:56
Hallo,

Zitat von Luckie:
Zum ersten Fall:
Ich werfe in so einem Fall eine Exception, dich ich dann im Aufrufer abfange.
Da fallen mir dann 2 Realisierungsmöglichkeiten ein. Angenommen ich lagere so eine Abarbeitung in eine eigene Prozedur aus und im Fehlerfall werfe ich eine Exception. Wenn ich jetzt die einzelnen Fehler unterscheiden möchte, könnte ich entweder verschiedene Exception-Typen ableiten und verwenden oder einen Typ und die Art des Fehlers dann in der Message der Exception hinterlegen.
Eingene Exception-Typen wärend da sauberer wegen einer möglichen Behandlung durch:
Delphi-Quellcode:
except
  on ... do
  on ... do
Richtig?

Zitat von Luckie:
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.
Kann nicht auch z.B. die Erstellung des 3. Objektes fehlschlagen? Das wäre dann nicht abgefangen. Ich meine unter .NET hat man ja immernoch den Garbage Collector als Sicherheitsnetz...

Zitat von jbg:
Alternativ kannst du auch alle Variablen vor dem ersten try auf NIL setzen...
Jetzt muß ich nochmal dumm nachfragen. Sind Objektvariablen direkt nach der Deklaration ohne vorherige Verwendung nicht immer mit NIL initialisiert?


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
Stephan Fietzek
  Mit Zitat antworten Zitat
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#5

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:02
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
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#6

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:07
Zitat von TPok:
Jetzt muß ich nochmal dumm nachfragen. Sind Objektvariablen direkt nach der Deklaration ohne vorherige Verwendung nicht immer mit NIL initialisiert?
Nur Variablen, die eine Referenzzählung haben: String, dyn. Array, IInterface, Variant.


Zitat:
Dazu fällt mir noch eine Frage ein:
Wenn ich mir z.B. im MSDN .NET-Beispielcode anschaue (meist in C#)
Unter .NET gibt es keine deterministischen Destruktoren. Diese werden aufgerufen, wenn der GC meint, er müsste jetzt Rechenzeit verbrauchen. Ein (natives) Delphi.Win32 Programm besitzt keinen GC. Somit musst du dich selbst ums Aufräumen kümmern. Hast dann aber auch den Vorteil, dass du deine Destruktoren "deterministisch" aufrufen kannst. Um wenigstens ein wenig Determinismus bei .NET Destruktoren zu haben (die übrigens Finalize() heißen), hat man das IDispose Interface implementiert. Das muss man dann aber auch selbst aufrufen. Bei Datei-Streams ist es zum Beispiel etwas blöd, wenn die Datei erst geschlossen wird, wenn der GC mal Zeit hat.


Mittels Interfaces kann man sich AutoPtr (C++) basteln. (muss ich mal schnell was zusammenschreiben...)
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#7

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:08
Zitat von TPok:
Kann nicht auch z.B. die Erstellung des 3. Objektes fehlschlagen?
Dann wird aber automatisch der Destructor aufgerufen.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von Lemmy1
Lemmy1

Registriert seit: 28. Nov 2004
Ort: Ismaning
184 Beiträge
 
Delphi 2006 Professional
 
#8

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:14
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;
Daniel
www.nemu.com - The N64 Emulator
  Mit Zitat antworten Zitat
jbg

Registriert seit: 12. Jun 2002
3.483 Beiträge
 
Delphi 10.1 Berlin Professional
 
#9

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:18
Hier mal meine schnell zusammengetippte AutoPtr Klasse:
Delphi-Quellcode:
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.
Und hier, wie man den AutoPtr nutzt
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
  Mit Zitat antworten Zitat
TPok

Registriert seit: 19. Jul 2004
Ort: Eisenach
25 Beiträge
 
Delphi 2005 Professional
 
#10

Re: Guter Stil oder 'Wie programmiere ich vernünftig?'

  Alt 19. Apr 2006, 01:21
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
Stephan Fietzek
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:42 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz