Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   TOLEEnum Umwandlung aus String (https://www.delphipraxis.net/216139-toleenum-umwandlung-aus-string.html)

Humbucker 4. Nov 2024 13:29

TOLEEnum Umwandlung aus String
 
Hallo Forum,
ich benötige eine Funktion zur Umwandlung eines String in einen Enumerator. Bei der Umwandlung stoße ich auf das Problem, dass der Typ des TOLEnum als tkInteger identifiziert wird. Daher versucht die Systemroutine GetEnumValue den eingehenden String als Integer zu casten, was natürlich schief geht. Über RTTI komme ich erst gar nicht zur der Systemroutine GetEnumValue. Was kann ich tun? Danke für eure Hilfe.

Code:
country := Map.GetCountryCode<CountryCode2>('DE'); //erwartetes Ergebnis: Enumerator CountryCode2_DE aus Unit PDF_Xpansion_Wrapper_16_TLB;

class function Map.GetCountryCode<T>(aLKZ: String) : T;
var
   lLKZ : string;
begin
   ......
   lLKZ := 'CountryCode2_' + copy(lLKZ,1,2); //nur die ersten beiden Stellen übertragen = USA -> US
   Result := TEnumUtils.GetEnumFromString<T>(lLKZ);
end;

class function TEnumUtils.GetEnumFromString<T>(aEnumString: String) : T;
    var
      lValue   : T;
  begin
    lValue := TRttiEnumerationType.GetValue<T>(aEnumString); --> Fehlermeldung: Ungültige Typumwandlung (TOLEEnum = tkInteger <> tkEnumeration)
    Result := T(lValue);
end;


unit System.Rtti;

class function TRttiEnumerationType.GetValue<T{: enum}>(const AName: string): T;
var
  v: Integer;
begin
  case PTypeInfo(TypeInfo(T))^.Kind of
    tkEnumeration:
      case System.TypInfo.GetTypeData(TypeInfo(T))^.OrdType of
        otUByte, otSByte: PByte(@Result)^ := GetEnumValue(TypeInfo(T), AName);
        otUWord, otSWord: PWord(@Result)^ := GetEnumValue(TypeInfo(T), AName);
        otULong, otSLong: PInteger(@Result)^ := GetEnumValue(TypeInfo(T), AName);
      end;
  else
    raise EInvalidCast.CreateRes(@SInvalidCast);
  end;
end;


unit PDF_Xpansion_Wrapper_16_TLB;

// Konstanten für enum CountryCode2
type
  CountryCode2 = TOleEnum;
const
  CountryCode2_unk = $00000000;
  CountryCode2_AL = $0000414C;
  CountryCode2_AD = $00004144;
  CountryCode2_AT = $00004154;
  CountryCode2_BA = $00004241;
  CountryCode2_BG = $00004247;
  CountryCode2_CA = $00004341;
  CountryCode2_CN = $0000434E;
  CountryCode2_HR = $00004852;
  CountryCode2_CZ = $0000435A;
  CountryCode2_CY = $00004359;
  CountryCode2_DK = $0000444B;
  CountryCode2_EE = $00004545;
  CountryCode2_FI = $00004649;
  CountryCode2_FR = $00004652;
  CountryCode2_DE = $00004445;
 ....


unit WinAPI.ActiveX

TOleEnum = type LongWord;

Uwe Raabe 4. Nov 2024 13:54

AW: TOLEEnum Umwandlung aus String
 
Du verwendest einmal
Delphi-Quellcode:
TEnumUtils.GetEnumToString<T>
, deklariert ist aber
Delphi-Quellcode:
TEnumUtils.GetEnumFromString<T>
.

Allerdings wird der eigentliche Grund der sein, dass
Delphi-Quellcode:
type
  CountryCode2 = TOleEnum;
keine Enumeration im Sinne von Delphi darstellt, bei der die einzelnen Werte als Strings dargestellt werden. Intern ist das ein normaler Integer gedanklich kombiniert mit einer Liste von Integer-Konstanten, aber eben kein Typ mit dem TRttiEnumerationType etwas anfangen kann.

Humbucker 5. Nov 2024 10:02

AW: TOLEEnum Umwandlung aus String
 
Hallo Uwe,
danke für deine Nachricht. Ich habe die Funktion umbenannt, aber das ist in der Tat nicht das Problem.
Es ist genau wie du sagst, die Enumeratoren werden von Delphi nicht als solche erkannt.
Gibt es dann überhaupt eine Möglichkeit über den Bezeichner den Wert zu erhalten?
Vielleicht ist es eine sinnvolle Vorgehensweise die (benötigten) Enumeratoren neu zu definieren und dann als TOLEEnum zu casten?

Code:
// Konstanten für enum CountryCode2
type
  CountryCode2 = TOleEnum;
const
  CountryCode2_unk = $00000000;
  CountryCode2_AL = $0000414C;
  CountryCode2_AD = $00004144;
  ...
Nachdem was ich bisher gelesen habe, ist damit aber nicht mehr möglich mit RTTI zu arbeiten.

Gruß Michael

Uwe Raabe 5. Nov 2024 10:57

AW: TOLEEnum Umwandlung aus String
 
Eine syntaktisch funktionierende Lösung wäre eine Deklaration in etwa so:
Delphi-Quellcode:
type
{$SCOPEDENUMS ON}
  TCountryCode = (unk, AL, AD, AT, BA, BG, ...);
Damit würde TRttiEnumerationType.GetValue<TCountryCode>(AStrin g) einen AString wie z.B. "AL" oder "BG" in den entsprechenden TCountryCode umwandeln, also TCountryCode.AL bzw. TCountryCode.BG (Das "CountryCode2_" Prefix kann man sich dann sparen).

Allerdings muss man dann noch die Umsetzung in die entsprechenden TOleEnum-Werte realisieren. Dazu bietet sich eine record helper an, in dem man auch gleich die String-Umwandlung unterbringen kann:
Delphi-Quellcode:
type
  TCountryCodeHelper = record helper for TCountryCode
  private const
    cOleEnums: array[TCountryCode] of Cardinal = (
      CountryCode2_unk,
      CountryCode2_AL,
      CountryCode2_AD,
      ...
      );
    function GetAsOleEnum: Cardinal;
    function GetAsString: string;
    procedure SetAsOleEnum(const Value: Cardinal);
    procedure SetAsString(const Value: string);
  public
    property AsOleEnum: Cardinal read GetAsOleEnum write SetAsOleEnum;
    property AsString: string read GetAsString write SetAsString;
  end;

function TCountryCodeHelper.GetAsOleEnum: Cardinal;
begin
  Result := cOleEnums[Self];
end;

function TCountryCodeHelper.GetAsString: string;
begin
  Result := TRttiEnumerationType.GetName<TCountryCode>(Self);
end;

procedure TCountryCodeHelper.SetAsOleEnum(const Value: Cardinal);
begin
  for var idx := Low(cOleEnums) to High(cOleEnums) do begin
    if cOleEnums[idx] = Value then begin
      Self := idx;
      Exit;
    end;
  end;
  Self := TCountryCode.unk;
end;

procedure TCountryCodeHelper.SetAsString(const Value: string);
begin
  try
    Self := TRttiEnumerationType.GetValue<TCountryCode>(Value);
    if Ord(Self) < Ord(Low(TCountryCode)) then
      raise EInvalidCast.CreateRes(@SInvalidCast);
  except
    on EInvalidCast do
      Self := TCountryCode.unk;
  end;
end;
Die Verwendung ist dann schon deutlich aufgeräumter, aber dafür muss man schon einen gewissen Aufwand treiben.

Rollo62 5. Nov 2024 14:17

AW: TOLEEnum Umwandlung aus String
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1542869)
Delphi-Quellcode:
type
  TCountryCodeHelper = record helper for TCountryCode
  private const
    cOleEnums: array[TCountryCode] of Cardinal = (
      CountryCode2_unk,
      CountryCode2_AL,
      CountryCode2_AD,
      ...
      );
    function GetAsOleEnum: Cardinal;
    function GetAsString: string;
    procedure SetAsOleEnum(const Value: Cardinal);
    procedure SetAsString(const Value: string);
  public
    property AsOleEnum: Cardinal read GetAsOleEnum write SetAsOleEnum;
    property AsString: string read GetAsString write SetAsString;
  end;

Hallo Uwe,

ich nutze sowas in der Art, aber verzichte auf die Properties.

Delphi-Quellcode:
type
  TCountryCodeHelper = record helper for TCountryCode
    function AsOleEnum : Cardinal;
    function AsString : String;
  end;
Welchen Vorteil hätte ich, wenn ich an dieser Stelle Properties benutze?
Ich meine Funktionen machen das genauso, oder übersehe ich da etwas?


Sorry, es ging Dir ja um Setter und Getter, dann ist es klar.

Ich mache das mehr so, weil es nicht immer passende Konvertierungen gibt.
Delphi-Quellcode:
type
  TCountryCodeHelper = record helper for TCountryCode
    function ToOleEnum : Cardinal;
    function TryFromOleEnum( AVal : Cardinal ) : Boolean;    

    function ToString : String;
    function TryFromString( AVal : String ) : Boolean;
  end;

Uwe Raabe 5. Nov 2024 14:37

AW: TOLEEnum Umwandlung aus String
 
Zitat:

Zitat von Rollo62 (Beitrag 1542874)
Ich mache das mehr so, weil es nicht immer passende Konvertierungen gibt.

Bei den Settern ist das durchaus sinnvoll. Da es hier aber ein dediziertes Unknown gibt, kann man halt das als Fallback verwenden. Kommt immer auf die Verwendung an.

Die Getter müssen indes immer liefern können. Alles andere wäre ein Programmierfehler (zumindest bei dieser Art Anwendungsfälle). Bei der String-Rückgabe von unk könnte man über einen Leerstring diskutieren, aber das passt dann nicht mehr ganz zum RTTI-Ansatz.

OT: Ich bin eigentlich ein Freund von Properties. Die Anweisungen lassen sich dann einfacher mit dem Reverse Assignment Feature oder dem try-finally-Wizard umdrehen.

Rollo62 5. Nov 2024 17:31

AW: TOLEEnum Umwandlung aus String
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1542875)
OT: Ich bin eigentlich ein Freund von Properties.

Ja ich schon auch, aber der Mehraufwand ist halt auch da.
Ist aber richtig, das macht man nur einmal und dann ist es für immer drin :thumb:

Bei nicht vorhandenem Unknown wäre bei einem Getter ja nur noch eine Exception möglich,
was ich persönlich für den einfachen Enum ( <> Integer ) etwas zu hart finde.
Deshalb bevorzuge ich TryFromXyz, da kann ich dann explizit drauf reagieren.

Ich nutze das oft um enum => Integer oder enum => String und zurück umzuwandeln, z.B. für Serialisierung oder Persistance.
Dabei sollte es meiner Meinung nach besser einen 1:1 Match geben, auch bei Unknown, und keinen Fallback, was zu Misinterpretation führen könnte.

Nur wenn es eben nicht exakt in das Enum reinpasst, dann gäbe es den Abbruch bei TryFromXyz.
Das kann z.B. durch Versionsupdates oder ähnliches leicht passieren.

Humbucker 7. Nov 2024 16:55

AW: TOLEEnum Umwandlung aus String
 
Hallo Uwe,
ich habe den Record Helper verwendet und es funktioniert sehr gut. Vielen Dank für den Beispielcode. Auch an alle Anderen vielen Dank für eure Hilfe.
VG Michael :-D

Humbucker 8. Nov 2024 10:40

AW: TOLEEnum Umwandlung aus String
 
Hallo Uwe,

ich habe deinen Code ausprobiert und bin dabei auf folgendes Problem bei der Rückwandlung des TOLEEnum in einen String gestoßen. Bei der Verwendung der Funktion GetasString wird die als TCountryCode gecastete Variable von CountryCode2 übergeben (CountryCode2_DE = 17477).

Zitat:

Zitat von Uwe Raabe (Beitrag 1542869)
Delphi-Quellcode:
function TCountryCodeHelper.GetAsString: string;
begin
  Result := TRttiEnumerationType.GetName<TCountryCode>(Self); --> liefert falsche Werte, da Self keinen Indexwert von TCountryCode darstellt
end;

Das funktioniert leider nicht (allerdings ohne Fehlermeldung bei der Ausführung), da der Wertebereich von TCountryCode überschritten wird. Gebe ich einen gültigen Indexwert von TCountryCode in die Funktion (CountryCode_DE = 15) erhalte ich das gewünschte Ergebnis.

Als Alternative hatte ich mir folgenden Code überlegt (ich habe die Variablen cOLEEnums umbenannt)

Delphi-Quellcode:
function TCountryCodeHelper.GetCountryAsString: string;
begin
  Result := 'CountryCode_unk';
  for var idx := Low(cCountryEnums) to High(cCountryEnums) do begin
    if cCountryEnums[idx] = Cardinal(Self) then begin
      Result := TRttiEnumerationType.GetName<TCountryCode>(idx);
      break;
    end;
  end;
end;
Da Cardinal(Self) mit dem Wert 69 in der Funktion ankommt, ist eine Iteration mit Vergleich über cCountryEnums nicht möglich. Wie kann ich den ursprünglich übergebenen Wert 17477 der Funktion erhalten? Vielen Dank.

VG Michael

Uwe Raabe 8. Nov 2024 10:52

AW: TOLEEnum Umwandlung aus String
 
Zitat:

Zitat von Humbucker (Beitrag 1542984)
Problem bei der Rückwandlung des TOLEEnum in einen String gestoßen. Bei der Verwendung der Funktion GetasString wird die als TCountryCode gecastete Variable von CountryCode2 übergeben (CountryCode2_DE = 17477).

Kannst du bitte zeigen was du da genau machst? Der Begriff als TCountryCode gecastete Variable macht mich schon stutzig.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:11 Uhr.
Seite 1 von 2  1 2      

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