![]() |
Arbeiten mit überladenen Operatoren
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo!
In neueren Delphi-Versionen sind Records mehr als nur ![]() ![]() ![]() ![]() Ich habe eine kleine Demo-Anwendung erstellt, welche den einfachen Vergleich von Versionsnummern demonstriert. Oftmals liegen diese nicht in verwertbarer numerischer Form vor sondern als Strings wie 1.0 oder 25.0.29899.2631. Will man zwei Versionsnummern vergleichen, muss man den String in vier numerische Werte zerlegen und diese einzeln vergleichen. Dass dies nicht ganz so trivial ist wie es auf den ersten Blick scheint, zeigt so manche ![]() Wie schön wäre es, wenn man Versionsnummern direkt mit einander vergleichen könnte. Das klappt zwar auf den ersten Blick erstaunlich gut:
Delphi-Quellcode:
Aber wie so oft steckt der Teufel hier im Detail. Denn schon eine geringfügig andere Schreibweise der an sich identischen Information lässt diesen Vergleich fehlschlagen:
begin
if '1.0' > '0.1' then ShowMessage('1.0 ist größer als 0.1'); end;
Delphi-Quellcode:
Denn "1.0.0" ist nun mal gleich "1.0", aber nicht größer. Dennoch erscheint hier die Message. Der Vergleich ergibt ein False-Positive.
begin
if '1.0.0' > '1.0' then ShowMessage('1.0.0 ist größer als 1.0'); end; Also müsste man Delphi beibringen, die beiden Strings anders zu vergleichen. Genau das erreiche ich im beigefügten Beispiel durch überladene Operatoren eines Records. Durch einen simplen Typecast lassen sich die Versions-Strings dann korrekt vergleichen:
Delphi-Quellcode:
Folgende Operatoren habe ich in dem vorliegenden Beispiel implementiert:
begin
if TVersion('1.0.0') > TVersion('1.0') then ShowMessage('1.0.0 ist größer als 1.0'); end;
Code:
Während die ersten sechs allesamt Vergleichsoperatoren sind, ist Implicit ein Zuweisungsoperator. Dadurch wird es möglich, einer TVersion-Variablen direkt einen String zuzuweisen:
= (Equal)
> (Greater) >= (GreaterOrEqual) < (Less) <= (LessOrEqual) <> (NotEqual) := (Implicit)
Delphi-Quellcode:
Ohne einen Implicit-Operator würde dies mit einem Compilerfehler "Inkompatible Typen" fehlschlagen. Der Einfachheit halber erledigt Implicit auch gleich das Zerlegen in die vier numerischen Werte, welche über die Record-Properties MajorMS, MajorLS, MinorMS und MinorLS zugreifbar sind.
var
LString: string; LVersion: TVersion; begin LString := '1.0'; LVersion := LString; end; Ein überladener Vergleichsoperator hat immer zwei Parameter: Die linke und die rechte Seite, meist als "A" und "B" benannt:
Delphi-Quellcode:
Weil wir im vorliegenden Fall aber auch direkt mit Strings vergleichen wollen, muss jeder überladene Vergleichsoperator drei Mal implementiert werden:
class operator Equal(A: TVersion; B: TVersion): Boolean;
Delphi-Quellcode:
Zu guter Letzt gibt es noch einen weiteren Zuweisungsoperator namens Explicit, welcher immer dann greift, wenn man wie oben gezeigt mit Typecasts arbeitet:
class operator Equal(A: TVersion; B: TVersion): Boolean;
class operator Equal(A: TVersion; B: string): Boolean; class operator Equal(A: string; B: TVersion): Boolean;
Delphi-Quellcode:
Im Beispiel ist jeder überladene Zuweisungsoperator zweimal implementiert:
begin
if TVersion('1.0.0') > TVersion('1.0') then ShowMessage('1.0.0 ist größer als 1.0'); end;
Delphi-Quellcode:
Das hat den einfachen Grund, dass die Zuweisung auch in beiden Richtungen funktioniert:
class operator Implicit(AString: string): TVersion;
class operator Implicit(AVersion: TVersion): string;
Delphi-Quellcode:
oder man (vereinfacht ausgedrückt) Strings und TVersions synonym verwenden kann:
var
LString: string; LVersion: TVersion; begin LString := '1.0'; LVersion := LString; LString := LVersion; end;
Delphi-Quellcode:
Für den Fall dass man auf der einen Seite numerische Werte vorliegen hat und auf der anderen Seite einen String, lassen sich die Properties MajorMS, MajorLS, MinorMS und MinorLS auch schreibend verwenden:
var
LVersion: TVersion; begin LVersion := '1.0'; if LVersion = '1.0.0' then ShowMessage('LVersion ist gleich 1.0.0'); end;
Delphi-Quellcode:
Doch Moment! Warum verwende ich für Format() hier die Methode LVersion.ToString, wenn TVersion und String zuweisungskompatibel sind? Das liegt an der Implementierung von Format(): Der zweite Parameter ist ein
var
LVersion: TVersion; begin LVersion.MajorMS := 4; LVersion.MajorLS := 60; LVersion.MinorMS := 128; LVersion.MinorLS := 5; if LVersion > '4.21.9.2' then begin ShowMessage(Format('%s ist größer als 4.21.9.2', [LVersion.ToString])); end; end;
Delphi-Quellcode:
, also eine Liste untypisierter Variablen. Darum weiß der Compiler hier schlicht nicht, dass wir TVersion als String haben möchten. Deshalb greift der überladene Operator Implicit nicht und der Compiler würde sich über inkompatible Typen beschweren.
array of const
Die Methode ToString hat einen optionalen Parameter Elements, welcher angibt bis zu welcher Stelle wir die Versionsnummer zum String umwandeln möchten:
Delphi-Quellcode:
ergibt die Anzeige "4.60". Die Zuweisung der numerischen Elemente verhält sich dabei "intelligent":
var
LVersion: TVersion; begin LVersion.MajorMS := 4; LVersion.MajorLS := 60; LVersion.MinorMS := 128; LVersion.MinorLS := 5; ShowMessage(LVersion.ToString(2)); end;
Delphi-Quellcode:
ergibt die Anzeige "4.0.128". TVersion erkennt also, dass wir hier nur das erste und dritte Element zugewiesen haben und ergänzt selbstständig die "0" für das zweite Element.
var
LVersion: TVersion; begin LVersion.MajorMS := 4; LVersion.MinorMS := 128; ShowMessage(LVersion.ToString); end; Was soll eigentlich passieren, wenn man einen String zuweist, welcher keine gültige Versionsnummer enthält? Nun, das hängt von der Implementierung des Zuweisungsoperators Implicit ab. Im Beispiel löse ich der Einfachheit halber einfach eine EConvertError-Exception aus:
Delphi-Quellcode:
begin
try if TVersion('1.0') > '1.0 RC1' then begin end; except on EConvertError do begin ShowMessage('Da ging etwas schief'); end; end; end; Abschließend stellt sich noch die Frage, warum ich das Ganze so kompliziert löse. Es gibt den relativ einfachen Ansatz, die ![]() That's it! Zu Risiken und Nebenwirkungen fragen Sie Ihren Delphi-Compiler oder stecken den Kopf in den Sand ;-) |
AW: Arbeiten mit überladenen Operatoren
Schönes Beispiel mit Versionsnummern. Vektorrechnung wäre auch ein Kandidat gewesen.
Ein Link auf die Doku würde ich vielleicht noch einmal etwas prominenter am Schluss zum Nachschlagen: ![]() (Ja, ich weiß dass der auch oben im Fließtext vorkommt) |
AW: Arbeiten mit überladenen Operatoren
Ich war Anfangs noch auf dem Dreh dass Int64 für Versionsnummern ausreichen müsste und habe mit vier Words und einem
![]() Nun müsste mir nur noch einer der Delphi-Auguren erklären, weshalb überladene Operatoren ohne das Schlüsselwort overload auskommen :lol: EDIT: Zitat:
|
AW: Arbeiten mit überladenen Operatoren
Oder auch weshalb die Argumente
Delphi-Quellcode:
sein können, oder auch nicht. Und es trotzdem passt.
const
|
AW: Arbeiten mit überladenen Operatoren
Zitat:
|
AW: Arbeiten mit überladenen Operatoren
Zitat:
Delphi-Quellcode:
, bei denen entweder A oder B vom Record-Typ und der jeweils andere ein beliebiger Typ sein kann. Dann wird vom Compiler nach einer ADD-Operator-Implementierung für eine solche Konstellation gesucht.
A + B
So gesehen enthält das Schlüsselwort operator implizit schon das overload. |
AW: Arbeiten mit überladenen Operatoren
Sagen wir mal, es wirkt einfach in sich nicht schlüssig. Denn bei normalen Methoden gleichen Namens pocht der Compiler auf das Schlüsselwort overload. Allerdings scheint es ihn auch nicht sonderlich zu stören, wenn man es bei den class operators mit angibt.
Was mich gerade viel mehr stört: In einem meiner Projekte eingebunden bekomme ich die Compilerwarnung "Sprach-Feature nicht unterstützt: Explicit". Aber auch nur bei diesem Projekt. Ist dieses Feature von irgendwelchen Projekteinstellungen oder Compilerschaltern abhängig? |
AW: Arbeiten mit überladenen Operatoren
Deaktiviere mal in den Projekt-Optionen die Erzeugung der C++ Header-Dateien.
|
AW: Arbeiten mit überladenen Operatoren
EDIT: Danke, hat sich erledigt. Zwar war das Projekt auf "Nur DCUs erzeugen" eingestellt, aber es gab noch ein Package das auf alles erzeugen eingestellt war.
|
AW: Arbeiten mit überladenen Operatoren
Evtl auch interessant:
![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:52 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