Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Recordhelper - Methode mit gleichem Namen wie im Record (https://www.delphipraxis.net/197232-recordhelper-methode-mit-gleichem-namen-wie-im-record.html)

Neutral General 23. Jul 2018 14:07

Delphi-Version: 10 Seattle

Recordhelper - Methode mit gleichem Namen wie im Record
 
Hallo,

Das ist quasi eine Folgefrage zu TValue.AsVariant und Boolean - Fehler?
Da ich gerade (noch) nicht auf 10.2 umsteigen kann musste ich mir anders behelfen:
Delphi-Quellcode:
TValueHelper = record helper for TValue
public
  function AsVariant() : Variant;
end;

function TValueHelper.AsVariant: Variant;
begin
  if (IsType<Boolean>()) then
    Result := AsBoolean()
  else
    Result := Self.AsVariant(); // !
end;
Die markierte Zeile führt allerdings zu einer Endlosrekursion.
"inherited" funktioniert in dem Zusammenhang nicht und auch sonst habe ich keine Art gefunden wie ich das original TValue.AsVariant aufrufen kann.
Mein letzter und extremster Versuch:
Delphi-Quellcode:
function TValueHelper.AsVariant: Variant;
var originalAsVariant: function(Self: TValue): Variant;
begin
  originalAsVariant := @TValue.AsVariant;
  if (IsType<Boolean>()) then
    Result := AsBoolean()
  else
    Result := originalAsVariant(Self);
end;
Aber selbst in diesem Fall springt er wieder in den TValueHelper.AsVariant. Bedeutet das, dass das original-TValue.AsVariant nicht mehr existiert und nicht nur überdeckt sondern ersetzt wurde?
Denn so sieht es für mich momentan aus.

Die Frage ist nun: Existiert die ursprüngliche Methode noch und wenn ja, wie kann ich sie aufrufen?

Disclaimer: Mir ist klar, dass ich das ganze umgehen kann, indem ich meine Helper-Methode einfach umbenenne, aber einerseits wäre mir ein Überdecken lieber und andererseits bin ich jetzt neugierig was da jetzt im Hintergrund wirklich passiert :shock:

himitsu 23. Jul 2018 14:10

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Delphi-Quellcode:
  else
    Result := inherited Self.AsVariant(); // !
end;
:?:

Neutral General 23. Jul 2018 14:12

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Zitat:

Zitat von himitsu (Beitrag 1408411)
Delphi-Quellcode:
  else
    Result := inherited Self.AsVariant(); // !
end;
:?:

Zitat:

Zitat von Neutral General (Beitrag 1408410)
"inherited" funktioniert in dem Zusammenhang nicht [...]

:!:

Uwe Raabe 23. Jul 2018 14:28

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Das mit dem inherited funktioniert bei records ja leider nicht. In diesem Fall kann man das aber umgehen:
Delphi-Quellcode:
function TValueHelper.AsVariant: Variant;
begin
  if (IsType<Boolean>()) then
    Result := AsBoolean()
  else
    Result := AsType<Variant>;
end;

Neutral General 23. Jul 2018 14:49

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1408413)
Das mit dem inherited funktioniert bei records ja leider nicht. In diesem Fall kann man das aber umgehen:
Delphi-Quellcode:
function TValueHelper.AsVariant: Variant;
begin
  if (IsType<Boolean>()) then
    Result := AsBoolean()
  else
    Result := AsType<Variant>;
end;

Danke, so werd ichs machen! :)

Habe aber selbst noch etwas rumprobiert und auf Biegen und Brechen lässt es sich machen:
Delphi-Quellcode:
var
  OriginalTValueAsVariant: Pointer = @TValue.AsVariant; // Muss VOR dem Recordhelper deklariert/zugewiesen werden

type
  TValueHelper = record helper for TValue
  public
    function AsVariant() : Variant;
  end;

function TValueHelper.AsVariant: Variant;
type TAsVariantFunc = function(Self: TValue): Variant;
begin
  if (IsType<Boolean>()) then
    Result := AsBoolean()
  else
    Result := TAsVariantFunc(OriginalTValueAsVariant)(Self);
end;
Deine Lösung ist natürlich deutlich besser/eleganter und werde die auch auf jeden Fall verwenden, aber man hat ja nicht immer das Glück, dass man so nen schönen Ausweg hat.
For Science und so.

himitsu 23. Jul 2018 14:57

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Jupp, vor dem Helper, da wo es noch nicht überschrieben ist, oder aus der TypeInfo/RTTI auslesen.

Aber es ist doch ein Bug, dass inherited nicht geht?
Nja, aber stimmt schon, inherited wird hier auf den Vorfahren der Klasse und nicht auf den "Vorfahren" des Helpers gehen.

Allergings ist es im Normalfall eine schlechte Idee eine bestehende Funktion mit den Helpern verdecken zu wollen.
Es ist schwer zu erkennen ob/wann der Helper genommen wird oder das Original des Typs.




Wäre es nicht toll, wenn die Helperbugs mal nach all den Jahren behoben werden? (vorallem mehrere Helper an einem Typ)

Stevie 23. Jul 2018 16:13

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Man kann das ja nicht immer so machen, wie in diesem Fall richtigerweise von Uwe vorgeschlagen.

Daher einfach ausnutzen, dass bei einer Redefinition eines Typs der helper nicht berücksichtigt wird

Delphi-Quellcode:
type
  TValue2 = type TValue;
  TValueHelper = record helper for TValue
  public
    function AsVariant: Variant;
  end;

function TValueHelper.AsVariant: Variant;
begin
  if IsType<Boolean> then
    Result := AsBoolean
  else
    Result := TValue2(Self).AsVariant();
end;
Natürlich darauf achten, dass TValue2 vor dem Helper definiert wird, dadurch kennt er unten beim Hardcast nicht die helper Methode und ruft die aus System.Rtti auf.

himitsu 23. Jul 2018 16:35

AW: Recordhelper - Methode mit gleichem Namen wie im Record
 
Jain, aber du kannst aus der TypeInfo/RTTI den Zeiger der gewünschten Methode auslesen und diese dann direkt aufrufen, am Helper/Compiler vorbei. :zwinker:

Und bei dieser Art des Override ist es halt manchmal ungünstig, dass es "unbemerkt" nur dann überschrieben/überdeckt wird, wenn der Helper sichtbar ist.


Bei uns hatte ich mal TField.Value mit einem Helper überschrieben, da Value:=Null nicht bei allen TField-Nachfolgern "richtig" funktioniert.
Aber nach anfänglichen Problemchen wurde es dann doch als "zusätzliches" Property implementiert und nicht als soeine Überladung des eigentlichen Value-Property.
Delphi-Quellcode:
type
  TFieldHelper = class helper for TField
  private
    function GetAsValueNullable : Variant;
    procedure SetAsValueNullable(const NewValue : Variant);
  public
    {$REGION 'Documentation'}
    ///   <summary>
    ///     <para>
    ///       Liest immer NULL, wenn das Feld NULL ist und setzt auch die NULL.
    ///     </para>
    ///     <para>
    ///       siehe TWideMemoField.GetAsVariant ... macht aus NULL ein '',<br />aber TWideStringField belässt es als
    ///       NULL, aber nur im Getter und nicht im Setter.
    ///     </para>
    ///   </summary>
    {$ENDREGION}
    property ValueNullable : Variant read GetAsValueNullable write SetAsValueNullable;
    {$REGION 'Documentation'}
    ///   <summary>
    ///     <para>
    ///       Achtung: Value=Variant, aber bei IsNull liefert/setzt es oftmals keine NULL.<br />z.B. TWideMemoField macht
    ///       dataus einen Leersting ''<br />siehe ValueNullable
    ///     </para>
    ///     <para>
    ///       Achtung: Property TField.Value=Variant, aber TIntegerField.Value=Integer<br />Property wird der Vererbung
    ///       überdeckt.
    ///     </para>
    ///   </summary>
    {$ENDREGION}
    //property Value; deprecated; // aus unerfindlichen Gründen meint der Compiler dass Variant, GetAsVariant und SetAsVariant nicht im Vorfahren existieren
    //property Value: Variant read GetAsVariant write SetAsVariant; deprecated;
    property Value: Variant read _GetAsVariant write _SetAsVariant; //deprecated 'BUGFIX: use ValueNullable if Value can contains NULL-Values';
  end;

function TFieldHelper.GetAsValueNullable: Variant;
begin
  if IsNull then
    Result := Null
  else
    Result := Value;
end;

procedure TFieldHelper.SetAsValueNullable(const NewValue: Variant);
begin
  if VarIsNull(NewValue) then
    Clear
  else
    Value := NewValue;
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:38 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz