Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Variable ist möglicherweise nicht initialisiert worden (https://www.delphipraxis.net/199597-variable-ist-moeglicherweise-nicht-initialisiert-worden.html)

Codehunter 5. Feb 2019 08:46

Delphi-Version: 10.2 Tokyo

Variable ist möglicherweise nicht initialisiert worden
 
Hallo!

Ich frage mich schon länger, ob die Validierung bzgl. der Warnung "Variable 'foo' ist möglicherweise nicht initialisiert worden" nicht ein überempfindliches Mimöschen ist. Ein Beispiel:
Delphi-Quellcode:
var
  I: Integer;
  LEncoding: TEncoding;
  LEncodings: TStringList;
begin
  LEncodings := TStringList.Create;
  try
    for I := 1 to 6 do begin
      case I of
        1: LEncoding := TEncoding.ANSI;
        2: LEncoding := TEncoding.ASCII;
        3: LEncoding := TEncoding.BigEndianUnicode;
        4: LEncoding := TEncoding.Unicode;
        5: LEncoding := TEncoding.UTF7;
        6: LEncoding := TEncoding.UTF8;
      end;
      LEncodings.AddObject(LEncoding.EncodingName, LEncoding); // <-- Warnung bzgl. "LEncoding"
    end;
    TOpenTextFileDialog(Sender).Encodings.Assign(LEncodings);
  finally
    FreeAndNil(LEncodings);
  end;
end;
Da fällt mir nun wirklich keine Möglichkeit ein, wie "LEncoding" nicht initialisiert sein könnte. Ist der Compiler nicht intelligent genug, die For-Schleife und das den Schleifenzähler bedingende Case in eine Verbindung zu setzen? Oder kann man das in der Konfiguration beeinflussen? Außer natürlich, die Warnung komplett abzuschalten, was ich keinesfalls möchte.

Grüße
Cody

Uwe Raabe 5. Feb 2019 08:52

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Codehunter (Beitrag 1424707)
Ist der Compiler nicht intelligent genug, die For-Schleife und das den Schleifenzähler bedingende Case in eine Verbindung zu setzen?

Nein, in diesem Fall nicht. Ist ja auch nicht ganz einfach, oder hast du auf Anhieb einen entsprechenden Algorithmus parat?

Würden für die Grenzen der For-Schleife Variablen verwendet, wäre es sogar noch komplizierter. Dann müsste man die Belegung der Variablen analysieren.

Es sollte aber gehen, wenn du in das case einen else-Zweig mit einer Exception einbaust.

Delphi-Quellcode:
      case I of
        1: LEncoding := TEncoding.ANSI;
        2: LEncoding := TEncoding.ASCII;
        3: LEncoding := TEncoding.BigEndianUnicode;
        4: LEncoding := TEncoding.Unicode;
        5: LEncoding := TEncoding.UTF7;
        6: LEncoding := TEncoding.UTF8;
      else
        raise EProgrammerNotFound.Create('WTF?');
      end;

Neutral General 5. Feb 2019 08:54

AW: Variable ist möglicherweise nicht initialisiert worden
 
Naja das ist halt ein sehr sehr spezieller Fall, den man in der Realität selten hat.

Bei Schleifen gibt es oft keine Garantie, dass diese durchlaufen werden, weil die Liste oder das Array was man durchläuft leer sein kann.
Und ein case muss zusätzlich nicht alle Fälle abdecken. Von daher ist eine Variable die nur innerhalb eines case in einer Schleife initialisiert wird und vorher/nachher nicht mehr allgemein eine mögliche Fehlerquelle.
Der Compiler ist in deinem speziellen Fall nicht so schlau, genau zu erkennen, dass du eine statische Schleife hast mit einem case, dass alle möglichen Fälle abdeckt.
Bzw. er ist wahrscheinlich mit Absicht nicht so schlau, weil solche Fälle in "freier Wildbahn" so selten vorkommen, dass es einfacher ist nur grob drüber zu gucken, als jedes Konstrukt bis ins tiefste zu analysieren für die 0,1% der Fälle wo kein Problem auftreten kann.

Codehunter 5. Feb 2019 09:21

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1424708)
Nein, in diesem Fall nicht. Ist ja auch nicht ganz einfach, oder hast du auf Anhieb einen entsprechenden Algorithmus parat?

Ich bin nur für das Aufstöbern solcher Glitches und das Meckern zuständig ;-) Also zumindest was meine Quellen angeht habe ich den Fall gar nicht so selten. Eigentlich bin ich ja dankbar für die Warnung. Nur in Fällen wie diesen muss ich ein eigentlich überflüssiges ELSE in das CASE einbauen und dazu noch einen Kommentar, damit ich in drölfzig Jahren auch noch weiß, warum.

Uwe Raabe 5. Feb 2019 09:33

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Codehunter (Beitrag 1424713)
Nur in Fällen wie diesen muss ich ein eigentlich überflüssiges ELSE in das CASE einbauen

Ein ELSE ist niemals überflüssig - eher das Ergebnis jahrelanger Erfahrung.

Schokohase 5. Feb 2019 09:36

AW: Variable ist möglicherweise nicht initialisiert worden
 
Das Else ist nicht überflüssig, denn es ist ein Schutz, falls mal jemand auf die dusselige Idee kommt und einen Case entfernt. Ohne Else bekommst du eine Warnung und mit Else gibt es eine Exception wenn nicht alle Fälle behandelt wurden.

Das ist ein Feature.

p80286 5. Feb 2019 10:40

AW: Variable ist möglicherweise nicht initialisiert worden
 
Da war der schokohase schneller. Das else sollte immer dabei sein. Genauso wie ich immer eine Basisinitialisierung vornehme:
Delphi-Quellcode:
begin
  max:=cMax;
  if a>20 then max:=50
  else max:=100;
........
Ist eigentlich überflüssig, aber sicher.

Gruß
K-H

Codehunter 5. Feb 2019 10:48

AW: Variable ist möglicherweise nicht initialisiert worden
 
Und das nachdem man jahr(zehnt)elang geübt hat, möglichst schlanken und übersichtlichen Code zu schreiben ^^

Schokohase 5. Feb 2019 11:18

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Codehunter (Beitrag 1424726)
Und das nachdem man jahr(zehnt)elang geübt hat, möglichst schlanken und übersichtlichen Code zu schreiben ^^

Ich will dir ja den Tag nicht verderben, aber das ist auf jeden Fall schlanker.
Wegen der Übersichtlichkeit (und im Hinblick auf Geschwindigkeit) würde ich das ganze sogar noch ganz anders angehen.
Delphi-Quellcode:
var
  LEncodings: TStringList;
  lEncoding: TEncoding;
begin
  LEncodings := TStringList.Create;
  try
    for lEncoding in TArray<TEncoding>.Create(TEncoding.ANSI, TEncoding.ASCII, TEncoding.BigEndianUnicode, TEncoding.Unicode, TEncoding.UTF7, TEncoding.UTF8) do
      LEncodings.AddObject(lEncoding.EncodingName, lEncoding);

    TOpenTextFileDialog(Sender).Encodings.Assign(LEncodings);

  finally
    LEncodings.Free();
  end;
end;

p80286 5. Feb 2019 11:44

AW: Variable ist möglicherweise nicht initialisiert worden
 
Nichts für ungut, das erinnert mich an diesen Einzeilercode, den es vor 30 Jahren in jeder Computerfachzeitschrift gab. Die erste Version scheint mir wesentlich übersichtlicher.

Gruß
K-H

Schokohase 5. Feb 2019 11:53

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von p80286 (Beitrag 1424736)
Nichts für ungut, das erinnert mich an diesen Einzeilercode, den es vor 30 Jahren in jeder Computerfachzeitschrift gab. Die erste Version scheint mir wesentlich übersichtlicher.

Das ist kein Einzeiler-Code, denn meiner besteht aus mehreren Zeilen.

Codehunter 5. Feb 2019 11:57

AW: Variable ist möglicherweise nicht initialisiert worden
 
Ist das Zebra nun schwarz mit weißen Streifen oder weiß mit schwarzen Streifen? ^^

Der Code im Erstpost war ein Beispiel, an dem ich mein Anliegen leicht demonstrieren konnte. Im "richtigen Leben" sind die Dinge ungleich komplexer.

Schokohase 5. Feb 2019 12:10

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Codehunter (Beitrag 1424739)
Ist das Zebra nun schwarz mit weißen Streifen oder weiß mit schwarzen Streifen? ^^

Es ist weiß und hat schwarze Streifen, denn diese schwarzen Streifen entstehen dadurch dass Pigmente das Fell schwarz färben.

Moombas 5. Feb 2019 12:36

AW: Variable ist möglicherweise nicht initialisiert worden
 
Zitat:

Zitat von Schokohase (Beitrag 1424742)
Zitat:

Zitat von Codehunter (Beitrag 1424739)
Ist das Zebra nun schwarz mit weißen Streifen oder weiß mit schwarzen Streifen? ^^

Es ist weiß und hat schwarze Streifen, denn diese schwarzen Streifen entstehen dadurch dass Pigmente das Fell schwarz färben.

Andererseits hat das Zebra schwarze Haut, es könnte also eigentlich eher ein schwarzes Zebra mit weißen Streifen sein :P


BTT:
Schreibt man den "Einzeiler" von Schokohase etwas anders ist er dennoch übersichtlich:
Delphi-Quellcode:
var
  LEncodings: TStringList;
  lEncoding: TEncoding;
begin
  LEncodings := TStringList.Create;
  try
    for lEncoding in TArray<TEncoding>.Create(TEncoding.ANSI,
                                              TEncoding.ASCII,
                                              TEncoding.BigEndianUnicode,
                                              TEncoding.Unicode,
                                              TEncoding.UTF7,
                                              TEncoding.UTF8) do
      LEncodings.AddObject(lEncoding.EncodingName, lEncoding);

    TOpenTextFileDialog(Sender).Encodings.Assign(LEncodings);
  finally
    LEncodings.Free();
  end;
end;
Es ist oft nur eine Sache des Schreibstils, der die Übersichtlichkeit ändert.

Uwe Raabe 5. Feb 2019 12:46

AW: Variable ist möglicherweise nicht initialisiert worden
 
Der Vorteil bei der Schreibweise mit dem generischen Array ist, daß Elemente nur an einer Stelle hinzugefügt bzw. entfernt werden müssen. Auch das Ändern der Reihenfolge ist einfacher. Bei der For-Schleife mit case-Anweisung sind immer zwei Dinge zu beachten. Fügt man nur ein neues case-Label ein und vergisst, die For-Schleife anzupassen, wundert man sich, warum das neue Encoding nicht angezeigt wird. Löscht man eines der case-Label ohne die For-Schleife anzupassen, ergibt die besagte Warnung plötzlich durchaus einen Sinn.

Codehunter 6. Feb 2019 09:34

AW: Variable ist möglicherweise nicht initialisiert worden
 
Am gezeigten Beispiel habt ihr natürlich recht. Das generische Array kann man glaub ich auch noch per
Delphi-Quellcode:
[TEncoding.ANSI, TEncoding.ASCII {...}]
abkürzen. Das for-in-[...]-Konstrukt benutze ich inzwischen sogar sehr gerne (schreibe sogar eigene Enumeratoren). Das einzige das mir da fehlt, ist eine Entsprechung zum alten for-a=b-downto-c-Konstrukt.

Dennis07 6. Feb 2019 13:40

AW: Variable ist möglicherweise nicht initialisiert worden
 
Dazu möchte ich das DocWiki zitieren:
Zitat:

Sie können sich nur auf den letzten Wert eines for-Schleifenzählers verlassen, wenn die Schleife mit einer goto- oder exit-Anweisung verlassen wird.
Man könnte den Wert vor dem Schleifendurchlauf initialisieren. Häufig ist es aber effizienter, ein
Delphi-Quellcode:
Exit
zu verwenden und den Default-Wert erst nach dem durchlaufen zu setzen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:36 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