![]() |
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. |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Die einfachste Art ist doch hier
Delphi-Quellcode:
Wenn man das jetzt noch in einen Record packt, der die Umwandlung vornimmt, dann hat man doch alles zusammen und kann nach Belieben hin- und her konvertieren und das auch noch voll automatisch.
type
TMeinBeispiel = ( mbEins, mbZwei, mbDrei ); const MeinBeispielValues : array[TMeinBeispiel] of integer = ( 0, 1, 5 ); |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Sorry, aber irgendwie verstehe ich Dein Beispiel nicht ... oder ich stehe auf dem Schlauch.
Delphi-Quellcode:
aber was mache ich damit? Die Zuordnung
MeinBeispielValues[mbDrei] = 5
Delphi-Quellcode:
hatte ich doch vorher schon?
Ord(mbDrei) = 5
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Nein.
Delphi-Quellcode:
:zwinker:
Ord(mbDrei) = 2
Der Enum ist "einfach", besitzt eine vollständige RTTI, aber diehn nur der als Namensliste. Die eigentlichen Werte sind aber extern abgelegt und werden nur für's Casten benutzt. |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Ähm ...
Delphi-Quellcode:
type TMeinBeispiel = (mbEins=0, mbZwei=1, mbDrei=5);
begin ShowMessage(IntToStr(Ord(mbDrei))); // -> "5" |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Zitat:
[edit] "hatte" ... hatte "hab" gelesen. :oops: Aber zusätzlich hast du so eben einen Code, wo man alle gültigen "Namen" auslesen kann und auch eine Liste (Array) mit allen gültigen Werten hat. |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Da sieht man mal wieder, weshalb Enums 'böse' sind.
Ich würde eine Funktion schreiben, die über ein 'CASE' die Validität des Integers prüft und mich dann um die wichtigen Dinge kümmern.
Delphi-Quellcode:
Das ist häßlich, aber leicht verständlich, und schnell umgesetzt. Klar, kommt ein Enum dazu, muss ich das die case-label anpassen erweitern.
Function IsValidEnum (aValue : Integer) : Boolean;
Begin case TMyEnum(aValue) Of myFirstEnum, mySecondEnum, ... myLastEnum : result := True; else result := False; end end; Vereinfachen (z.B. über RTTI) würde ich das nie, denn das verleitet dann zum Verwenden von Enums auch bei neuen Projekten. |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Die Lösung von Dejan Vu ist für meinen Spezialfall vermutlich die passendste. Hatte da seltsamerweise nicht selbst dran gedacht, aber ich hatte ja ohnehin wohl fälschlicherweise angenommen, dass Delphi da eine eigene Abfragemöglichkeit mitbringt.
Die Möglichkeit per Array-Konstante von Sir Rufo würde natürlich auch gehen (mit dem Vorteil die Werte iterieren zu können), aber letzlich müssen die Werte dann getrennt vom Enum gepflegt werden (bei längeren Aufzählungen blos nicht die Werte vertauschen ;)) und zum Gültigkeits-Abfrage brauche ich ja doch wieder eine zusätzliche Funktion. Aber ich habe wieder was gelernt und habe eine Lösung. Vielen Dank Euch Dreien!! |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Statt der Funktion hatte ich mir, die ein/zwei Mal, wo ich sowas brauchte, eine Konstante direkt bei dem Typen deklariert.
Ist bei Änderungen auffälliger, als die Funktion außerhalb des Blickfeldes.
Delphi-Quellcode:
type
TMyEnum = (a, b, c, z=25); const cMyEnumRange = [a, b, c, z]; //cMyEnumRange: set of TMyEnum = [a, b, c, z]; var e: TMyEnum; s: set of TMyEnum; if not (e in cMyEnumRange) then raise Exception.Create('nö'); if s - cMyEnumRange <> [] then raise Exception.Create('nö'); |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Können Enums nur Werte <=255 annehmen? Ansonsten eine sehr schöne Idee, zumal die Konstante direkt beim Enum deklariert werden kann: Wenn schon Schrott, dann wenigstens an einer Stelle. :thumb:
|
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Zitat:
![]() |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Das SET nutzt Befehle der CPU und dort gibt es nur ein Byte als Index ... darum auch nur 0 bis 255.
Darum funktioniert das SET für WideChar nicht mehr. :cry: Der ENUM kann mindestens einen Integer als Speicher nutzen (also bis 32 Bit ... ob 64 Bit auch geht, weiß ich grade nicht auswendig) SET: 0 <= x < 2^8 (maximal 32 Byte adressierbar) ENUM: 0 <= x < 2^32 (vielleicht auch 64, aber ich glaub eher nicht) |
AW: Prüfen ob Integer im Enumeration-Type enthalten ist
Also ist das keine allgemeingültige Lösung.
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:18 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