![]() |
Delphi-Version: XE
Prüfen ob Integer im Enumeration-Type enthalten ist
Angenommen ich habe folgendes:
Delphi-Quellcode:
type TMeinBeispiel = (mbEins=0, mbZwei=1, mbDrei=5);
Jetzt erhalte ich einen Integer i aus einer externen Quelle (Ini, DB, etc.) und möchte diesen zu TMeinBeispiel casten. Per
Delphi-Quellcode:
funktioniert dies immer. Auch dann, wenn i gar nicht in TMeinBeispiel vorhanden ist. Dann hat MeinBeispiel schlicht den Wert i angenommen (der Debugger sagt "out of bound (i)").
MeinBeispiel := TMeinBeispiel(i);
Nun würde ich gerne bei Werten, die nicht in der Aufzählung vorhanden sind, einen Standardwert vorgeben. Wie kann ich nun aber prüfen, ob ein Wert enthalten ist (0, 1, 5: True; 3, 6: False)? Sowas wie
Delphi-Quellcode:
funktioniert ja leider nicht.
if i in TMeinBeispiel then
Muss ich den unschönen Umweg über GetEnumName/GetEnumValue gehen oder gibt es eine elegantere Lösung? |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Für Emums man man maximal in der RTTI abfragen was der größte Wert ist, aber nur, wenn das ein Enum ohne Wertdefinitionen ist, denn dann gibt es in der RTTI keine Namensliste.
Also gerade dein Beispiel ist so nicht und niemals lösbar. Zitat:
Delphi-Quellcode:
existiere vollständige RTTI-Infos.
type TMeinBeispiel = (mbEins, mbZwei, mbDrei);
Ansonsten kann man nur den maximalen Wertebereich prüfen und nichts die einzelnen "Werte". für Enums: 0..255, 0..65535 oder 0..4294967295 für Sets: 0..7, 0..15, 0..31, 0..63, ... bis maximal 0..255 Der Compiler rundet alles auf den nächst größeren kleinstmöglichen Speichertypen, also bei deinem TMeinBeispiel (als Enum) ist das genau ein Byte und somit passt in den Typen grundsätzlich erstmal alles rein, von 0 bis 255 und als Set 0 bis 7. Zitat:
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Gerade wenn die Bedeutung zwischen internem und externem System ausgetauscht werden muss, empfiehlt sich ein eigener DatenTyp um so auch typischer im Kontext zu bleiben.
Delphi-Quellcode:
Und man benutzt das dann einfach
unit Unit2;
interface type TMeinBeispiel = record private const VALID_VALUES: array [0 .. 2] of Integer = ( 0, 1, 5 ); class function GetValue( const Index: Integer ): TMeinBeispiel; static; public class function Values: TArray<TMeinBeispiel>; static; class property Eins: TMeinBeispiel index 0 read GetValue; class property Zwei: TMeinBeispiel index 1 read GetValue; class property Drei: TMeinBeispiel index 2 read GetValue; public class operator implicit( const a: Integer ): TMeinBeispiel; class operator implicit( const a: TMeinBeispiel ): Integer; class operator Equal( const a, b: TMeinBeispiel ): Boolean; class operator NotEqual( const a, b: TMeinBeispiel ): Boolean; // hier können noch weitere Operatoren definiert werden, je nach Belieben private FValue: Integer; public constructor Create( const Value: Integer ); property Value: Integer read FValue; end; implementation uses System.SysUtils; { TMeinBeispiel } constructor TMeinBeispiel.Create( const Value: Integer ); var LIdx: Integer; begin for LIdx := Low( VALID_VALUES ) to High( VALID_VALUES ) do if Value = VALID_VALUES[LIdx] then begin FValue := Value; Exit; end; raise EConvertError.CreateFmt( '%d kein gültiger Wert für TMeinBeispiel', [Value] ); end; class operator TMeinBeispiel.Equal( const a, b: TMeinBeispiel ): Boolean; begin Result := a.FValue = b.FValue; end; class function TMeinBeispiel.GetValue( const Index: Integer ): TMeinBeispiel; begin Result := TMeinBeispiel.VALID_VALUES[Index]; end; class operator TMeinBeispiel.implicit( const a: Integer ): TMeinBeispiel; begin Result := TMeinBeispiel.Create( a ); end; class operator TMeinBeispiel.implicit( const a: TMeinBeispiel ): Integer; begin Result := a.FValue; end; class operator TMeinBeispiel.NotEqual( const a, b: TMeinBeispiel ): Boolean; begin Result := not( a = b ); end; class function TMeinBeispiel.Values: TArray<TMeinBeispiel>; var LIdx: Integer; begin SetLength( Result, Length( TMeinBeispiel.VALID_VALUES ) ); for LIdx := Low( TMeinBeispiel.VALID_VALUES ) to High( TMeinBeispiel.VALID_VALUES ) do begin Result[LIdx] := TMeinBeispiel.VALID_VALUES[LIdx]; end; end; end.
Delphi-Quellcode:
Unzulässige Typen werden mit einer Exception direkt beim Umwandeln quittiert.
procedure DoWithValue( AValue : TMeinBeispiel );
begin // irgendwas damit machen end; begin DoWithValue( 5 ); // <- Wert aus der Datenbank ist ein einfacher Integer end. |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Huiuiui, da werden aber große Geschütze aufgefahren! :shock:
Ich dachte, mein Lösungsansatz (GetEnumName/GetEnumValue) sei zu umständlich und es müsse doch irgendwie einfacher gehen. Aber bei Sir Rufos Lösung (Vielen Dank für die Mühe!) bin ich doch etwas baff. Ich habe jetzt mal meinen Ansatz ausprobiert:
Delphi-Quellcode:
Aber das geht wohl nur, wenn TMeinBeispiel keine manuelle Indizes-Anpassung erhält, sonst "Type 'TMeinBeispiel' has no type info". Das ist wohl das, was himitsu meinte.
var
MeinBeispiel: TMeinBeispiel; begin MeinBeispiel := GetEnumMeinBeispielDefault(i); ... function GetEnumMeinBeispielDefault(const value: Integer): TMeinBeispiel; var s: String; i: Integer; begin s := GetEnumName(TypeInfo(TMeinBeispiel), value); i := GetEnumValue(TypeInfo(TMeinBeispiel), s); //s enthält "Speichermüll", wenn value nicht in TMeinBeispiel, statt leer zu sein if i>0 then Result := TTrayAction(value) else Result := mbEins; //Default-Wert end; Wie gesagt war meine ursprüngliche Hoffnung, dass es sowas wie
Delphi-Quellcode:
geben müsste, deren Syntax mir nicht bekannt ist. Letztlich sind in der Aufwählung einige Werte manuell festgelegt (0,1,5) und ich möchte gegen diese einen anderen Wert vergleichen.
if i in TMeinBeispiel then
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Ich stelle mir immer die Frage nach Typsicherheit und der Bequemlichkeit nach der "Anstrengung". Du kannst dir auch einen Record erstellen der zwischen dem ENUM und dem korrespondierendem Integer-Wert vermittelt. Alle Schnittstellen benutzen den Record, die Anwendung den ENUM und die Datenbank den Integer. Das geht auch.
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Wie gesagt, ohne Typ-Info bleibt nur noch die Speichergröße.
Und die Typinfo fehlt, weil man zu blöd ist und es nicht schafft "fehlende" Werte in die Namensliste aufzunehmen. :roll: Zitat:
Zitat:
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Beim Debuggen sehe ich:
Delphi-Quellcode:
Der Debugger stellt hierbei doch auch irgendwie fest, ob der Wert unter den vorgegebenen Werten ist oder nicht. Man selbst kann das aber nicht tun? :(
mb := TMeinBeispiel(1); //mb = mbZwei
mb := TMeinBeispiel(4); //mb = (out of bound) 4 |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
k.A. wo der Debugger die Werte her holt, aber in der einkompilierten RTTI fehlt ganz einfach die Liste der Namen, sobald man selber die Werte zuweist.
Und die neue erweiterte RTTI geht bei Enums IMHO auch nur auf die alte RTTI. Es wird dafür ein Array verwendt, wie man es z.B. von der Registry und anderen WinAPIs kennt.
Delphi-Quellcode:
'NameFürWert0'#0'NameFürWert1'#0'NameFürWert2'#0'NameFürWert3'#0#0
Und wenn man jetzt Werte weg lässt, dann entstünde #0#0, was ja dem Listenende entspricht, und die Liste wäre unvollständig/kaputt. Darum lässt der doofe Compiler/Linker diese Liste einfach ganz weg, anstatt z.B. einen "Dummy"-Wert einzufügen oder eine andere Speicherstruktur zu benutzen. :stupid: |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Zitat:
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Es werden aber nicht die Zahlen gespeichert, sondern die Namen, in einem indizierten Array. (Index = Zahl :stupid:)
Als Zahl ist alles im Wertebereich des Speichertyps gültig. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:13 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 by Thomas Breitkreuz