Entwickler-Ecke

Sonstiges (Delphi) - Sauberes Programmieren


freedy - Mo 25.10.04 13:24
Titel: Sauberes Programmieren
Hallo Forum!

Ich habe zwar schon probiert, einen Artikel über die Suche zu finden, hab aber leider keine Treffer erzielt.

Ich verwende Funktionen, die Variablen übergeben bekommen. Nehmen wir einfach mal an, die Funktion xy bekommt drei Variablen:

xy(a, b, c);

Mit den Werten a und c muss ich auch sonst umgehen. Die Variable b hingegen ist nur ein Dummy, damit ich die Funktion bzw. Prozedur ausführen kann.

Natürlich meldet Delphi beim Compilieren:
[Hinweis] dateiname.pas(218): Auf 'b' zugewiesener Wert wird niemals benutzt

Wie löst ihr solche Probleme?
freedy

PS: Die Prozedur xy() kann ich leider nicht umschreiben, weil b manchmal auch nicht als Dummy fungiert.


MrSaint - Mo 25.10.04 13:30

Also wenn du den Parameter "b" auch nur manchmal brauchst, kannst du daran nix optimieren... Lass den Compiler einfach meckern, das geht net besser...


MrSaint


jasocul - Mo 25.10.04 13:42

Diese Compiler-Meldungen kannst du in die Tonne treten.
Sobald du mit Try-Except-Blöcken arbeitest, fängt der Compiler noch mehr an rumzuspinnen. Bemängelt dann Variablen, die gar nicht benutzt werden, obwohl das einfach falsch ist.


freedy - Mo 25.10.04 14:19

Aber macht das Sinn, alles in die Tonne zu kloppen? Hm... *grübel*
Wir versuchen halt, unser Projekt möglichst sauber und nach Delphi-StyleGuide zu proggen.
D.h. für uns:
- keine Hinweise
- keine Warnungen
- keine Fehler (klar, sonst würd's ja nicht zu compilieren sein)

Gruß
freedy


jasocul - Mo 25.10.04 14:26

Es spricht nichts dagegen, die Hinweise zu prüfen. Nur stimmen die halt nicht immer.
Ansonsten kann ich nur empfehlen, die Prozeduren so zu halten, dass du von vornherein den Überblick behältst.


Stübi - Mo 25.10.04 14:30

Bei uns gilt auch ganz klar die Regel Keine Hinweise und Warnungen, jedoch ist das Firmenintern und ich darf auch nur Englische coments geben :cry:
Wie du das für die handhaben willst musste selbst wissen.

Gruss Stübi


Motzi - Mo 25.10.04 14:45

Also in 98% aller Fälle sind Warnungen und Hinweise durchaus gerechtfertigt. Sollte es dennoch mal passieren, dass der Compiler einen Fehler/eine Warnung ausgibt obwohl man genau weiß, dass diese an dieser Stelle überflüssig ist, so kann man die Hinweise und Warnungen per Compiler-Schaltern an diesen Stellen deaktivieren. Siehe dazu in der OH unter $HINTS bzw $WARNINGS


jasocul - Mo 25.10.04 14:46

Ich geb euch jetzt mal ein Beispiel:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
  try
    Screen.Cursor := crHourGlass;
    sl := TStringList.Create;
...
  finally
    Screen.Cursor := crDefault;
    sl.Free;
  end;

Da bekomme ich eine Warnung, dass die Variable sl nicht initialiert wurde.
Soll ich sowas ernst nehmen???


Motzi - Mo 25.10.04 14:49

Das hat einen ganz einfachen Grund... was passiert, wenn VOR der Zeile:

Delphi-Quelltext
1:
sl := TStringList.Create;                    

eine Exception auftritt? Richtig, er geht weiter in den finally-Teil und in diesem Fall wäre sl definitiv nicht initialisiert. Daher erzeugt man normalerweise zuerst das Objekt und öffnet erst dann den try-finally Block.


.Chef - Mo 25.10.04 14:50

Ich versuch ja auch immer, alles ohne Meldungen hinzukriegen. Aber zum Beispiel habe ich öfters "Combining signed and unsigned values. Widened both operants." (oder so). Unter einigem Aufwand würde man diese Meldungen zwar wegkriegen, aber der Übersichtlichkeit des Codes ist nicht geholfen, wenn man z.B. um jedes Length() noch ein Cardinal() schreibt.

Gruß,
Jörg


heinze - Mo 25.10.04 14:52

Mal kleiner Anfängeridee

Quelltext
1:
2:
3:
b:=b+1-1;
//oder am schlus der function 
b:=0;


freedy - Mo 25.10.04 14:53

jasocul hat folgendes geschrieben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
  try
    Screen.Cursor := crHourGlass;
    sl := TStringList.Create;
...
  finally
    Screen.Cursor := crDefault;
    sl.Free;
  end;



deshalb würde ich das auch so schreiben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  sl := TStringList.Create;
  try
    Screen.Cursor := crHourGlass;
  
...
  finally
    Screen.Cursor := crDefault;
    sl.Free;
  end;


Er erreicht auf jeden Fall den finally-Block. Von daher gibt es auch keine Fehlermeldung.


jasocul - Mo 25.10.04 14:58

Es sei denn, ich bekomme einen Fehler bei Create.


jojo-sp - Mo 25.10.04 15:08

Das läuft auf ein Zeit paradoxon hinaus, man kann die Vergangenheit nicht verändern! :-)
Aber ich hab auch so ein paar nette hinweise. Ich habe die Variable Hres lokal initialisiert, sie wird aber als "[Warnung] MainUnit.pas(76): Variable 'HRes' wurde wahrscheinlich nicht Initialisiert" ausgespuckt.


uall@ogc - Mo 25.10.04 15:09

dann macht man da sauch so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
 s1 := nil;
 try 
    Screen.Cursor := crHourGlass; 
    sl := TStringList.Create; 
... 
  finally 
    Screen.Cursor := crDefault; 
    if assigned(s1) then sl.Free; 
  end;


jasocul - Mo 25.10.04 15:12

Das wäre dann wirklich die sauberste Lösung.


freedy - Mo 25.10.04 15:30

Die Lösung von uall@ogc ist auf jeden Fall kleverer als meine... dann werde ich wohl heute mal eine Extraschicht einlegen müssen. :wink:

Ich bin ja bisher immer davon ausgegangen, dass Fehler, die in einem Create passieren, dort auch abgefangen werden. Klar, meine Prozedur läuft dann auch schief. Macht also Sinn.

Gruß
freedy


Nagelbrett - Mo 25.10.04 15:41

wobei man sich das if Assigned(sl) .. im finally dann auch sparen kann, da bei Aufruf von Free sowieso auf nil geprüft wird und es dann ja doppelt gemoppelt wäre


uall@ogc - Mo 25.10.04 15:48

hm jo


Motzi - Mo 25.10.04 19:39

3 Sachen: erstens überprüft Free (wie bereits erwähnt) intern sowieso ob der Self-Pointer nil ist, zweitens liefert ein Konstruktor sowieso nil zurück wenn eine Exception auftritt und 3tens was für ein Objekt willst du im finally-Teil freigeben wenn dieses gar nicht erst erstellt wurde weil eine Exception im Konstruktor aufgetreten ist?

Wenn es dir also darum geht auf Exceptions im Konstruktor zu reagieren müsste es so ausschaun:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
  try
    aObject = TMyClass.Create(..);
    try
      ...
    finally
      aObject.Free;
    end;
  except
    .. // auf Exception von Konstruktor reagieren
  end;


AndyB - Mo 25.10.04 20:10

Motzi hat folgendes geschrieben:
zweitens liefert ein Konstruktor sowieso nil zurück wenn eine Exception auftritt

Das macht er nicht. Er spring direkt zum nächsten Finally oder Except, womit die Variable uninitialisiert bleibt.


Motzi - Mo 25.10.04 20:21

AndyB hat folgendes geschrieben:
Motzi hat folgendes geschrieben:
zweitens liefert ein Konstruktor sowieso nil zurück wenn eine Exception auftritt

Das macht er nicht. Er spring direkt zum nächsten Finally oder Except, womit die Variable uninitialisiert bleibt.

Hast recht.. hatte das irgendwie anders in Erinnerung.. :?