![]() |
Delphi-Version: 10 Seattle
Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich bekomme bei folgendem
Delphi-Quellcode:
-Statement eine Warnung, dass eine Variable "möglicherweise" nicht initialisiert sein könnte:
case
Delphi-Quellcode:
Bei C++ ist der Compiler schlau genug:
type
TEnum = (a, b, c); var myEnum: TEnum; value: Byte; begin myEnum := TEnum.a; case myEnum of TEnum.a: value := 10; TEnum.b: value := 20; TEnum.c: value := 30; end; WriteLn(value); // W1036"might not have been initialized" // altough the case statement is exhaustive end.
Code:
Bei Sprachen wie Swift oder Rust bricht der Compiler mit einem Fehler ab, wenn ein match-Statement nicht vollständig ist.
enum Enum {a, b, c};
int main(int argc, char **argv) { Enum e = Enum::b; char c; switch (e) { case Enum::a: c = 1; break; case Enum::b: c = 2; break; case Enum::c: c = 3; break; } std::cout << c; return NO_ERROR; } Delphi bekommt von all dem aber nichts mit, und redet sich damit raus, dass es "möglicherweise" undefiniert sein könnte. Zumindest in 10.0 Seattle. Ist das in einer aktuellen Delphi-Version immer noch so? |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Jupp, denn Delphi weiß, dass in deinem Enum noch 253 weitere Werte sein können.
z.B.
Delphi-Quellcode:
myEnum := TEnum(123);
Sowas kannst du nur ausschließen, wenn bei Zuweisung an diese Variable der Wertebereich geprüft wird, und wenn man sämtliche Fehler ausschließen kann, z.B. böse Casts, fehlgeleitete Pointer, Buffer-Overflow, Häcker/Würmer/Viren, usw. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Oder es heißt dass der Teil der die Fehlermeldungen generiert gar nichts mehr von Enums weiß, da er nur noch mit blanken Zahlentypen arbeitet.
Oh mann... 😕 |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich füge dem case oft noch ein else hinzu, wobei dann im Debug eine Exception ausgelöst wird und im Release passiert nichts.
Kommt später was zur Aufzählung hinzu, so erinnert mich die Exception dann, das noch was fehlt. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
|
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich werfe die Exceptions immer, egal ob Debug oder Release.
Oder setze im Else die Variable auf NIL, was eh sein muß, weil ja sonst der Compiler von nicht-initialisierten Variablen schwafelt. Abgesehn da, wo eh immer nur ein Teil behandelt wird. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich setze IMMER ein "else", weil man ja schnell mal einen Typen ergänzt und dabei ein case statement vergisst.
Das macht es dann "sichtbarer", fehlertolerant und die Fehlersuche ist dann viel leichter als wenn man sich auf Compiler-Automatismen verlassen würde. Deswegen finde ich das Warning auch gut und richtig. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Wieso soll ich den immer alles definieren? Vielleicht will ich mit der Case ja nur Teile prüfen... Oder übersehen ich da etwas? Mavarik |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Genau aus diesen Gründen wird das in Rust und anderen Sprachen geprüft und genau deshalb werden diese an sicherheitsrelevanten Stellen gerne eingesetzt. Für den von dir beschriebenen Fall kann man z.B. mit if-Statements arbeiten. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich denke, ein 'Defaultwert' ist immer wichtig. Ob man den jetzt im else setzt oder (was ich inzwischen, auch weil ich beginne die Exit Anweisung einzusetzen, gerne mache) vor der Unterscheidung erstmal einen Wert zuzuweisen, ist in meinen Augen (fast) egal. Beim Else offenbart sich ein Fehler (d.h. eine 'unerwartete' Bedingung) eher, was in der Entwicklung wünschenswert, beim Kunden aber ggf. kontraproduktiv ist.
|
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Ich persönlich hätte auch etwas gegen einen Compilerfehler bei unvollständigem
Delphi-Quellcode:
. Es kommt schon häufiger vor, dass ich vorab per
case x of
Delphi-Quellcode:
eine Bereichsprüfung mache und dann mit einem
if x in [..] then
Delphi-Quellcode:
die verschiedenen (in diesem Bereich möglichen) Fälle auseinandernehme.
case x of
Gäbe es generell einen Fehler bei einem unvollständigen case-Statement wäre die else-Syntax ja auch überflüssig. Nichtsdestotrotz gibt es bei mir auch reichlich case-Konstrukte, bei denen im else-Zweig eine EProgrammerNotFound Exception geworfen wird. Die bereits anderswo erwähnten Unit-Tests sind dabei schon sehr hilfreich. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
|
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
|
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Delphi-Quellcode:
zu machen?
if x in [..]
Z.B. Performance, oder so? Ich meine ein "case x of" sollte immer am performantesten sein und das zusätzliche "if x in" überflüssig machen. Ok, bei großen Strukturen wird es unlesbarer ... |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Das hat eigentlich ganz andere Gründe. Nehmen wir z.B. einen Enum-Typ, der positive und negative Elemente enthält. Bei positiven bzw. negativen Werten sind spezifische Code-Teile auszuführen, die teilweise vom genauen Wert unabhängig sind, während andere Teile vom exakten Wert abhängen:
Delphi-Quellcode:
type
{$SCOPEDENUMS ON} TRating = (undefined, Perfect, OK, Acceptable, NotGoodEnough, Bad, TotalShit); TRatingHelper = record helper for TRating procedure HandleIncompleteCase(Rating: TRating); end; procedure TRatingHelper.HandleIncompleteCase(Rating: TRating); begin raise EProgrammerNotFound.Create('unhandled case for TRating: ' + TRttiEnumerationType.GetName(Rating)); end; const cRatingPositive = [TRating.Perfect, TRating.OK, TRating.Acceptable]; cRatingNegative = [TRating.NotGoodEnough, TRating.Bad, TRating.TotalShit]; procedure HandleRating(Rating: TRating); begin if Rating in cRatingPositive then begin // Handle positive rating part 1 ... // Handle specific rating case Rating of TRating.Perfect: ...; TRating.OK: ...; TRating.Acceptable: ...; else HandleIncompleteCase(Rating); end; // Handle positive rating part 2 ... end else if Rating in cRatingNegative begin // Handle negative rating part 1 ... // Handle specific rating case Rating of TRating.NotGoodEnough: ...; TRating.Bad: ...; TRating.TotalShit: ...; else HandleIncompleteCase(Rating); end; // Handle negative rating part 2 ... end else begin // Handle undefined rating ... end; end; |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
|
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Danke, das ist ein sehr schönes Beispiel :thumb: Aber trotzdem, ist es am Ende nicht fast das gleiche Ergebnis wie:
Delphi-Quellcode:
So richtig sehe ich den Mehrwert noch nicht.// Handle specific rating case Rating of //Positive rating TRating.Perfect: ...; TRating.OK: ...; TRating.Acceptable: ...; //Negative rating TRating.NotGoodEnough: ...; TRating.Bad: ...; TRating.TotalShit: ...; else HandleIncompleteCase(Rating); //<== OK, hier kann man nicht genau in Positive/Negative unterscheiden // Aber Du kommst ja trotzdem auf den richtigen, "falschen" case. // Wenn das "if x in" nicht passt, dann muss es auch nicht für "case x of" passen. end; type cRatingPositive = [TRating.Perfect..TRating.Acceptable]; // Wenn man es so definieren könnte, dann vielleicht. Ist das dann nicht eher ein Fall für ein Enum Set, mit allen seinen mathematischen Möglichkeiten? |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Diese ganze Diskussion ist eh nur theoretisch, da sie allenfalls case-Anweisungen ohne else-Zweig beträfe. Nur so könnte man das durchziehen ohne bestehenden Code plötzlich als fehlerhaft zu diffamieren. (Ich gehe dabei mal davon aus, dass der Entwickler weiß was er tut.) Insofern sind die im Code vorhandenen else-Zweige eigentlich auch obsolet und decken lediglich den Fall ab, dass die vorhergehende if-Anweisung klammheimlich erweitert und dabei die case-Anweisung nicht mitgezogen wird. Im Übrigen ist der Code auch nicht ganz korrekt in Bezug auf den record helper (und ein fehlendes then). Kommt davon wenn man Code im Web-Editor eingibt anstatt in der IDE. |
AW: Delphi merkt nicht wenn ein case-Statement vollständig ist, oder?
Zitat:
Der Vorab-Guard nimmt nur Performance und hilft meiner Meinung nach noch nicht mal bessere Lesbarkeit durch Gruppierung zu erzeugen. Ich bleibe bei Version HandleRating2. Trotzdem interessiert mich ein echtes Beispiel, wo das "if x in" gegenüber das "case x of" klar gewinnt, denn Ersteres nutze ich eigentlich so gut wie nie. Das liegt vielleicht daran, dass ich seit C sehr in das switch bzw. case of verliebt bin :oops: Das Beispiel habe ich mal entsprechend erweitert, ist ausführbar, um alle Fälle auszutesten:
Delphi-Quellcode:
type {$SCOPEDENUMS ON} //(1) Wegfall eines Enums ==> Compilierfehler // TRating = (undefined, Perfect, { OK, } Acceptable, NotGoodEnough, Bad, TotalShit); //(2) Zusätzliches, positives Enum NochWasGutes TRating = (undefined, Perfect, OK, NochWasGutes, Acceptable, NotGoodEnough, Bad, TotalShit); //(3) Zusätzliches, negatives Enum SehrSchlecht // TRating = (undefined, Perfect, OK, NochWasGutes, Acceptable, NotGoodEnough, Bad, SehrSchlecht, TotalShit ); //(4) Zusätzliches, negatives Enum am Ende SehrSchlecht OutOfRange // TRating = (undefined, Perfect, OK, NochWasGutes, Acceptable, NotGoodEnough, Bad, SehrSchlecht, TotalShit, OutOfRange ); //(5) Behandelt Undefined // TRating = (Undefined, Perfect, OK, NochWasGutes, Acceptable, NotGoodEnough, Bad, SehrSchlecht, TotalShit, OutOfRange ); TRatingHelper = record helper for TRating procedure HandleIncompleteCase; end; procedure TRatingHelper.HandleIncompleteCase; begin var LName := TRttiEnumerationType.GetName( Self ); raise Exception.Create('unhandled case for TRating: ' + LName ); end; const //cRatingPositive = [TRating.Perfect, TRating.OK, TRating.Acceptable]; //(A) Funktioniert nur, wenn die Ratings als SubRange definiert werden können cRatingPositive = [TRating.Perfect .. TRating.Acceptable ]; cRatingNegative = [TRating.NotGoodEnough .. TRating.TotalShit ]; //(B) Funktioniert noch besser, wenn die äußeren Grenzen mit High/Low definiert werden können // Starte NICHT ab Undefined bis zum letzten // cRatingPositive = [ Succ(Low(TRating)) .. TRating.Acceptable ]; // cRatingNegative = [ TRating.NotGoodEnough .. High(TRating), ]; //(C) Funktioniert noch besser, wenn die äußeren Grenzen mit High/Low definiert werden können // Starte NICHT ab Undefined bis zum letzten gültigen Enum // cRatingPositive = [ Succ(Low(TRating)) .. TRating.Acceptable ]; // cRatingNegative = [ TRating.NotGoodEnough .. TRating.TotalShit ]; procedure HandleRating1(Rating: TRating); begin if Rating in cRatingPositive then begin // Handle positive rating part 1 //<== (1) Kommt hier rein, wenn es einen neuen Positiven gibt // Handle specific rating case Rating of TRating.Perfect: ; TRating.OK: ; //<== (1) Compile-Error bei Wegfall eines Enums TRating.Acceptable: ; else Rating.HandleIncompleteCase; //(2)(3) Runtime-Error: "NochWasGutes", Wenn es einen neuen Positiven gibt end; // Handle positive rating part 2 end else if Rating in cRatingNegative then begin // Handle negative rating part 1 // Handle specific rating case Rating of TRating.NotGoodEnough: ; TRating.Bad: ; TRating.TotalShit: ; else Rating.HandleIncompleteCase; //(3)(4) Runtime-Error: "SehrSchlecht", Wenn es einen neuen Negativen gibt end; // Handle negative rating part 2 end else begin // (4)(5) Handle Undefined or OutOfRange rating Rating.HandleIncompleteCase; end; end; procedure HandleRating2(Rating: TRating); begin // Handle specific rating case Rating of TRating.Perfect: ; TRating.OK: ; //<== (1) Compile-Error bei Wegfall eines Enums TRating.Acceptable: ; // Handle specific rating TRating.NotGoodEnough: ; TRating.Bad: ; TRating.TotalShit: ; else Rating.HandleIncompleteCase; //(2)(3)(4)(5) Runtime-Error: "NochWasGutes", "SehrSchlecht", Zentral, für alle // Könnte notfalls hier in Gruppen zerlegt werden, was nur im Fehlerfall Performance braucht end; end; procedure Test; begin var LRating := TRating.NochWasGutes; // (2) // var LRating := TRating.SehrSchlecht; // (3) // var LRating := TRating.OutOfRange; // (4) // var LRating := TRating.Undefined; // (5) HandleRating1( LRating ); //RunTime-Fehler, wenn es was neues, Positives/Negatives gibt // HandleRating2( LRating ); //RunTime-Fehler, wenn es was neues, Positives/Negatives gibt end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:47 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