Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren (https://www.delphipraxis.net/216198-case-anweisung-zwingen-alle-elemente-des-typen-auszuprogrammieren.html)

lxo 19. Nov 2024 11:56

Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Hallo,

ich möchte bei einer Case-Anweisung einen Compiler-Fehler bekommen wenn ein neues Element der Enumeration hinzugefügt wird.
Damit der Schritt zwingend ausprogrammiert wird.
Delphi gibt da nicht mal eine Warnung oder Hinweis aus.
Ich hab es auch schon versucht mit einer Hilfsvariable um die Warnung W1036 zu bekommen und die Warnung in dem Block dann als Fehler kennzeichnen.
Funktioniert aber leider nicht wie erwartet.

Hat jemand eine Idee wie man das lösen könnte?

Delphi-Quellcode:
type
  TEnum = ( t1, t2, t3);
var
  lEnum: TEnum;
  lEnumCheck: Boolean;
begin
  {$WARN USE_BEFORE_DEF ERROR}

  lEnum := t1;
  case lEnum of
    t1: lEnumCheck := True;  
    t2: lEnumCheck := True;  
//    t3: lCheck := True;
  end;

  if lEnumCheck then //Warnung ist hier wenn t3 auskommentiert ist bekomme aber keinen Fehler, wenn t3 wieder drin ist ist die Warnung ganz am Ende der Prozedur. Packe ich das {$WARN USE_BEFORE_DEF DEFAULT} nach dem end dann kriege ich in jedem Fall ein Fehler.
  begin

 
  end;

  {$WARN USE_BEFORE_DEF DEFAULT}
end;

DeddyH 19. Nov 2024 12:07

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Wie wäre es mit einem ganz anderen Ansatz?
Delphi-Quellcode:
type
  TEnum = (t1, t2, t3);

const
  EnumCheck: array[TEnum] of Boolean = (true, true, false);
Wenn jetzt TEnum um weitere Elemente erweitert wird, muss zwangsläufig auch die Konstante erweitert werden. Eine andere Möglichkeit wäre so etwas:
Delphi-Quellcode:
function EnumCheck(Value: TEnum): Boolean;
begin
  case Value of
    t1: Result := true;
    t2: Result := true;
    else
      raise EForgottenValue.Create('Hamwa nich was vergessen?');
  end;
end;

Rollo62 19. Nov 2024 12:12

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Delphi-Quellcode:
type
  TEnum = ( t1, t2, t3);

const
  CEnum : array[ TEnum ] of String = ( 't1', 't2', 't3' ); //Knallt schon hier, wenn es nicht übereinstimmt

var
  lEnum: TEnum;
  lEnumCheck: Boolean;
begin
   CEnum[ t1 ];
end;
Schnell hingekrakelt, Tippfehler inklusive ...

@DeddyH: Ey, mach mal langsamer, damit ich auch noch eine Chance habe :-D

DeddyH 19. Nov 2024 12:13

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Das entspricht doch meinem ersten Vorschlag, oder nicht?

[edit]@Rollo62: ich gelobe Besserung :mrgreen:[/edit]

himitsu 19. Nov 2024 12:17

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
vielleicht so?
Delphi-Quellcode:
  case Value of
    t1: Result := true;
    t2: Result := true;
    {$IF High(TEnum) <> t2} {$MESSAGE Error 'peng'} {$IFEND}
  end;

lxo 19. Nov 2024 12:18

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von DeddyH (Beitrag 1543230)
Wie wäre es mit einem ganz anderen Ansatz?
Delphi-Quellcode:
type
  TEnum = (t1, t2, t3);

const
  EnumCheck: array[TEnum] of Boolean = (true, true, false);
Wenn jetzt TEnum um weitere Elemente erweitert wird, muss zwangsläufig auch die Konstante erweitert werden. Eine andere Möglichkeit wäre so etwas:
Delphi-Quellcode:
function EnumCheck(Value: TEnum): Boolean;
begin
  case Value of
    t1: Result := true;
    t2: Result := true;
    else
      raise EForgottenValue.Create('Hamwa nich was vergessen?');
  end;
end;

Das mit der Konstante mache ich auch schon.
Ich möchte aber gerne direkt beim case hängen bleiben.
Nur weil ich dann die Konstante erweitert habe, habe ich mein case ja nicht angepasst.
Wenn ich mehrere case-anweisungen habe die ich beachten muss, müsste ich ja so eine Zuweisung über jede case-Anweisung von dieser Enumeration machen.

Den zweiten Fall mache ich auch schon.
Das bekomme ich aber erst zur Laufzeit mit.
Hätte es halt gerne direkt beim kompilieren.
Unit-Test wäre das einzige was mir noch einfällt.
Hätte es aber wie gesagt gerne schon direkt beim kompilieren als Fehler, damit man direkt dahin geführt wird und das ausprogrammieren kann.

lxo 19. Nov 2024 12:31

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von himitsu (Beitrag 1543234)
vielleicht so?
Delphi-Quellcode:
  case Value of
    t1: Result := true;
    t2: Result := true;
    {$IF High(TEnum) <> t2} {$MESSAGE Error 'peng'} {$IFEND}
  end;

Geht schon mehr in die Richtung wie ich das haben will, aber auch noch nicht so die optimale Lösung die ich mir vorstelle.
Funktioniert z.B. nicht wenn jemand mittendrin ein Element einträgt (auch wenn man sowas lieber nicht machen sollte).
Und wenn man die Bedingung unten anpasst ohne den neuen Case-Fall hinzuzufügen würde man das ja auch schon umgehen.

Kann man denn evtl. mit den Compiler-Direktiven eine variable Hochzählen? Hab dazu nichts gefunden.
Damit könnte ich ja für jeden Case-Fall die Variable hochzählen und dann gegen "Ord(High(TEnum))" checken.

dummzeuch 19. Nov 2024 12:44

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von lxo (Beitrag 1543236)
Zitat:

Zitat von himitsu (Beitrag 1543234)
Delphi-Quellcode:
    {$IF High(TEnum) <> t2} {$MESSAGE Error 'peng'} {$IFEND}

Geht schon mehr in die Richtung wie ich das haben will, aber auch noch nicht so die optimale Lösung die ich mir vorstelle.
Funktioniert z.B. nicht wenn jemand mittendrin ein Element einträgt (auch wenn man sowas lieber nicht machen sollte).
Und wenn man die Bedingung unten anpasst ohne den neuen Case-Fall hinzuzufügen würde man das ja auch schon umgehen.

Kann man denn evtl. mit den Compiler-Direktiven eine variable Hochzählen? Hab dazu nichts gefunden.
Damit könnte ich ja für jeden Case-Fall die Variable hochzählen und dann gegen "Ord(High(TEnum))" checken.

Vielleicht ganz banal:

Delphi-Quellcode:
{$IF Ord(High(TENum) <> 1}{$MESSAGE Error 'peng'} {$IFEND}

lxo 19. Nov 2024 12:46

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von dummzeuch (Beitrag 1543237)
Zitat:

Zitat von lxo (Beitrag 1543236)
Zitat:

Zitat von himitsu (Beitrag 1543234)
Delphi-Quellcode:
    {$IF High(TEnum) <> t2} {$MESSAGE Error 'peng'} {$IFEND}

Geht schon mehr in die Richtung wie ich das haben will, aber auch noch nicht so die optimale Lösung die ich mir vorstelle.
Funktioniert z.B. nicht wenn jemand mittendrin ein Element einträgt (auch wenn man sowas lieber nicht machen sollte).
Und wenn man die Bedingung unten anpasst ohne den neuen Case-Fall hinzuzufügen würde man das ja auch schon umgehen.

Kann man denn evtl. mit den Compiler-Direktiven eine variable Hochzählen? Hab dazu nichts gefunden.
Damit könnte ich ja für jeden Case-Fall die Variable hochzählen und dann gegen "Ord(High(TEnum))" checken.

Vielleicht ganz banal:

Delphi-Quellcode:
{$IF Ord(High(TENum) <> 1}{$MESSAGE Error 'peng'} {$IFEND}

Ist doch erstmal das gleiche wie von himitsu oder wo soll da der Unterschied sein?

himitsu 19. Nov 2024 12:47

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Das ist so aber auch schwer mitzubekommen,
bzw. es wird nicht lesbarer. :stupid:
Delphi-Quellcode:
{$IF Ord(High(TEnum)) <> 1} {$MESSAGE Error 'peng'} {$IFEND}


Delphi-Quellcode:
array[TEnum] of ... = (...);

Hier bekommst du die Änderung der Anzahl mit.

Wenn jemand aber gleichzeitig etwas hinzufügt und löscht,
ohne dass es zwischendrin angepasst wurde,
dann flutscht das natürlich durch.

lxo 19. Nov 2024 12:52

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Meine Idee mit dem Ord(High(TEnum)) war so gedacht:

Delphi-Quellcode:
type
  TEnum = ( t1, t2, t3);
var
  lEnum: TEnum;
begin
  {$DEFINE ENUMCOUNT 0}
  case lEnum of
    t1:
     begin
      {$DEFINE ENUMCOUNT ENUMCOUNT+1}
      ...
     end;
    t2:
     begin
      {$DEFINE ENUMCOUNT ENUMCOUNT+1}
      ...
     end;
    // Hier immer letztes Element aus der Case-Anweisung eintragen.
    {$IF Ord(High(TEnum)) <> ENUMCOUNT} {$MESSAGE Error 'Case-Fall fuer jedes Element ausprogrammieren!'} {$IFEND}
  else
    raise Exception.Create('Fehlermeldung');
  end;
end;

sh17 19. Nov 2024 13:05

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von himitsu (Beitrag 1543239)
Das ist so aber auch schwer mitzubekommen,
bzw. es wird nicht lesbarer. :stupid:
Delphi-Quellcode:
{$IF Ord(High(TEnum)) <> 1} {$MESSAGE Error 'peng'} {$IFEND}


Delphi-Quellcode:
array[TEnum] of ... = (...);

Hier bekommst du die Änderung der Anzahl mit.

Wenn jemand aber gleichzeitig etwas hinzufügt und löscht,
ohne dass es zwischendrin angepasst wurde,
dann flutscht das natürlich durch.

Dann würde es aber auch knallen, weil ein alter Wert fehlt - wenn man sich bisher an seine Regel der vollständigen Auswertung gehalten hat.

dummzeuch 19. Nov 2024 16:42

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von lxo (Beitrag 1543238)
Zitat:

Zitat von dummzeuch (Beitrag 1543237)
Zitat:

Zitat von lxo (Beitrag 1543236)
Zitat:

Zitat von himitsu (Beitrag 1543234)
Delphi-Quellcode:
    {$IF High(TEnum) <> t2} {$MESSAGE Error 'peng'} {$IFEND}


Vielleicht ganz banal:

Delphi-Quellcode:
{$IF Ord(High(TENum) <> 1}{$MESSAGE Error 'peng'} {$IFEND}

Ist doch erstmal das gleiche wie von himitsu oder wo soll da der Unterschied sein?

Es erkennt auch, wenn jemand einen enum-Wert einfügt, nicht nur wenn jemand einen Wert anhängt. Bei beidem ändert sich Ord(High(TENum)), aber nur bei letzterem ändert sich High(TENum).

Der schöne Günther 19. Nov 2024 17:10

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Siehe auch:
Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?

und

Zitat:

Zitat von Der schöne Günther (Beitrag 1498589)
Bei Sprachen wie Swift oder Rust bricht der Compiler mit einem Fehler ab, wenn ein match-Statement nicht vollständig ist. Delphi bekommt von all dem aber nichts mit(...)


Rollo62 19. Nov 2024 17:19

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von lxo (Beitrag 1543235)
Hätte es halt gerne direkt beim kompilieren.

Zitat:

Zitat von lxo (Beitrag 1543226)
Hallo,
ich möchte bei einer Case-Anweisung einen Compiler-Fehler bekommen ....

Genau, beim Compilieren kommt der Fehler mit so einer Lösung
Delphi-Quellcode:
CEnum : array[ TEnum ] of String = ( 't1', 't2', 't3' ); //Knallt schon hier, wenn es nicht übereinstimmt
Dass man das jetzt erweitern muss, ist ja genau Sinn des Crashes.
Nebenbei hätte so eine noch weitere Vorteile, also zum Beispiel Textausgabe, Rückgabe beliebiger Werte, etc.

Eventuell sucht Du ja sowas, etwas "Verboseres":

Delphi-Quellcode:
type
  TMyEnum = (Value0, Value1, Value2);

const
  ExpectedEnumSize = 3; // Erwartete Anzahl der Elemente in TMyEnum

{$IF    Ord(High(TMyEnum)) > (ExpectedEnumSize-1) }
  {$MESSAGE ERROR 'TMyEnum wurde erweitert!'      }
{$ELSEIF Ord(High(TMyEnum)) < (ExpectedEnumSize-1) }
  {$MESSAGE ERROR 'TMyEnum wurde verkleinert!'    }
{$IFEND}

dummzeuch 19. Nov 2024 17:29

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von Rollo62 (Beitrag 1543254)
Genau, beim Compilieren kommt der Fehler mit so einer Lösung
Delphi-Quellcode:
CEnum : array[ TEnum ] of String = ( 't1', 't2', 't3' ); //Knallt schon hier, wenn es nicht übereinstimmt

Das Problem ist, dass man dann aber die betroffenen case-Statements alle manuell raussuchen muss, was nicht ganz so einfach ist, da dort ja nicht der Typ sondern eine Variable des Typs verwendet wird, man kann also nicht einfach ein grep auf den Namen des Typs machen. Wenn der Compiler bei jedem davon einen Fehler schmeißt, ist das einfacher. Kann ja auch gerne abschaltbar sein (wobei man das ja auch mit einem else erreichen könnte).

himitsu 19. Nov 2024 18:29

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Er muß ja keinen Fehler werfen, aber z.B. eine Warnung oder Info wäre eigentlich problemlos möglich, wenn bei einem Enum nicht alle Werte benutzt wurden.

Ja, natürlich auch abschaltbar, bzw. vielleicht über ein Attribut oder Keyword, explizit anforderbar,
denn es kommt vor, dass ich in manchen Case absichtlich garnicht alles verwende,
z.B. wenn an einer Stelle nur ein Teil benötigt wird und der Rest einfach nichts machen soll.

Uwe Raabe 19. Nov 2024 20:43

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von dummzeuch (Beitrag 1543255)
da dort ja nicht der Typ sondern eine Variable des Typs verwendet wird, man kann also nicht einfach ein grep auf den Namen des Typs machen.

Wenn man SCOPEDENUMS verwendet, schon...

bernau 20. Nov 2024 07:43

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1543272)
Wenn man SCOPEDENUMS verwendet, schon...

Wenn da nicht mein 25 Jahre alter Code wäre. :oops:

dummzeuch 20. Nov 2024 08:05

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von himitsu (Beitrag 1543256)
Ja, natürlich auch abschaltbar, bzw. vielleicht über ein Attribut oder Keyword, explizit anforderbar, denn es kommt vor, dass ich in manchen Case absichtlich garnicht alles verwende,
z.B. wenn an einer Stelle nur ein Teil benötigt wird und der Rest einfach nichts machen soll.

Dafür gibt's ja schon else. Dann muss man allerdings evtl. anderen Tools beibringen, dass ein leeres else durchaus OK ist.

Delphi-Quellcode:
case bla of
  b1: HandleB1;
  b2: HandleB2;
else
  // nix tun
end;

dummzeuch 20. Nov 2024 08:08

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1543272)
Zitat:

Zitat von dummzeuch (Beitrag 1543255)
da dort ja nicht der Typ sondern eine Variable des Typs verwendet wird, man kann also nicht einfach ein grep auf den Namen des Typs machen.

Wenn man SCOPEDENUMS verwendet, schon...

Wenn ich so viel tippen wollte, kann ich ja gleich Java oder C# verwenden. ;-)

Das wäre mal eine interessante IDE-Funktion: Man tippt einen Enum-Wert und die IDE schlägt automatisch einen Scope vor. Also quasi ein umgekehrtes TEnumType.<ctrl+Space>
Oder gibt's das am Ende schon?

Sinspin 20. Nov 2024 08:37

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von bernau (Beitrag 1543289)
Zitat:

Zitat von Uwe Raabe (Beitrag 1543272)
Wenn man SCOPEDENUMS verwendet, schon...

Wenn da nicht mein 25 Jahre alter Code wäre. :oops:

... wie ein guter Wein.

joachimd 20. Nov 2024 08:40

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von dummzeuch (Beitrag 1543291)
Das wäre mal eine interessante IDE-Funktion: Man tippt einen Enum-Wert und die IDE schlägt automatisch einen Scope vor. Also quasi ein umgekehrtes TEnumType.<ctrl+Space>
Oder gibt's das am Ende schon?

Ich meine ja. Müsste aber eine VM starten, um es zu verifizieren. Klappt aber nur beim Erstellen des Case, nicht im Nachhinein beim Erweitern des Enum.

dummzeuch 20. Nov 2024 08:51

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Zitat:

Zitat von joachimd (Beitrag 1543293)
Zitat:

Zitat von dummzeuch (Beitrag 1543291)
Das wäre mal eine interessante IDE-Funktion: Man tippt einen Enum-Wert und die IDE schlägt automatisch einen Scope vor. Also quasi ein umgekehrtes TEnumType.<ctrl+Space>
Oder gibt's das am Ende schon?

Ich meine ja. Müsste aber eine VM starten, um es zu verifizieren. Klappt aber nur beim Erstellen des Case, nicht im Nachhinein beim Erweitern des Enum.

Das ist leider nicht, was ich meinte:
Delphi-Quellcode:

type
  TEnumTyp = (enumWert1, enumWert2);

if SomeEnumVariable = enumWert1<Tastatur-Shortcut-drücken>
sollte daraus dann TEnumTyp.EnumWErt1 machen.

Uwe Raabe 20. Nov 2024 10:57

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
Aber das geht ja bereits. Es muss halt auch als SCOPEDENUM deklariert sein.

himitsu 20. Nov 2024 11:16

AW: Case-Anweisung - Zwingen alle Elemente des Typen auszuprogrammieren
 
Wobei ich mir das manchmal andersrum wünschen würde, also einen impliziten Default-Scope.

Der Compiler weiß, welchen Typ die Variable oder der Parameter haben, wo das reinsoll.
Da wäre es doch "intelligent" wenn der dann auch gleich den Scope vorgibt, wo zuerst nach dem Namen gesucht wird,
damit man z.B. bei einem SET nicht dutzende Male diesen beschissenen Scope mit angeben muß.

Außerdem würde es dann auch ohne ScopedEnum dann öffters den richtigen Enum erwischen, weil teilweise sind ja einige Namen doppelt/mehrfach vergeben.


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