Einzelnen Beitrag anzeigen

Incocnito

Registriert seit: 28. Nov 2016
223 Beiträge
 
#1

TObject As Interface

  Alt 16. Jul 2019, 12:23
Hi zusammen,

ich muss zugeben, ich weiß gerade nicht so recht, wie ich nach meinem Problem suchen kann,
daher poste ich hier, auch wenn es möglicherweise schon eine Antowrt dazu irgendwo gibt.

Es handelt sich hierbei um eine theoretische Frage, da ich das ganze im Projekt nun anders gelöst habe.
Trotzdem würde ich gerne wissen, was ich falsch mache oder wie man das Problem reell lösen könnte.

Ich habe eine vordefinierte Quelle in welchem ich Daten als "TObject" speichern kann. Ein StringGrid.
In diesem StringGrid speichere ich pro Zeile die Nutzinformationen in StringGrid.Objects[0, iRow].
Zum Speichern verwende ich einen eigenen Typ "TGridObject".
Nun möchte ich eine allgemeingültige Funktion haben, mit welcher ich im StringGrid einen Hint anzeigen kann.
Damit diese Funktion auch auf anderen Grids funktioniert definiere ich ein Interface "IGridInfo",
welches mir die Funktion "GetVal(const iCol : Integer) : String;" bereit stellen soll.
Die Idee ist, dass die "AutoGridHint"-Funktion den String, welcher angezeigt werden kann über diese Funktion erhällt.
Das Objekt muss dann natürlich von diesem Interface erben und die Funktion umsetzen.

Definition vom Interface in der Tools-Unit:
Delphi-Quellcode:
type
  IGridInfo = interface(IInterface)
    ['{675C1022-0376-4583-AF24-8FAA2D008139}']
    Function GetVal(const iCol : Integer) : String;
  end;
Definition des GridObjects in der Unit mit dem Form:
Delphi-Quellcode:
type
  TGridObject = class(TInterfacedObject, IGridInfo)
  private
    FRow : Integer;
    FSomeText : String;
    FSomeMore : String;
  public
    Function GetVal(const iCol : Integer) : String;
    Constructor Create(const iRow : Integer; const sText1 : String; const sText2 : String);
  end;
Das Grid (sgDaten) wird dann irgendwie irgendwann gefüllt und in Spalte "0" wird das Objekt gespeichert.
Delphi-Quellcode:
  currObj := TGridObject.Create(iRow, 'bla', 'blub');
  sgDaten.Objects[0, iRow] := currObj;
  sgDaten.Cells[1, iRow] := currObj.GetVal(1);
GetVal(1) soll hierbei FSomeText und GetVal(2) soll FSomeMore zurück geben.

In MouseMove vom Grid werden die Koordinaten umgerechnet und an die tolle Methode übergeben:
Delphi-Quellcode:
procedure TfrmMain.sgDatenMouseMove(
  Sender : TObject;
  Shift : TShiftState;
  X : Integer;
  Y : Integer
);
var
  ACol : Integer;
  ARow : Integer;
begin
  inherited;
  sgDaten.MouseToCell(x, y, ACol, ARow);
  AutoGridHint(sgDaten, ACol, ARow);
End;
Bisher bin ich bei solchen Sachen dann hingegangen und habe folgendes gemacht (was auch toll funktioniert):
Delphi-Quellcode:
procedure TfrmMain.sgDatenMouseMove(
  Sender : TObject;
  Shift : TShiftState;
  X : Integer;
  Y : Integer
);
var
  ACol : Integer;
  ARow : Integer;
  GridObj : TGridObject;
  sHint : String;
begin
  inherited;
  sgDaten.MouseToCell(x, y, ACol, ARow);

  if (sgDaten.Objects[0, ARow] <> nil) AND
     (sgDaten.Objects[0, ARow] IS TGridObject) then
  Begin
    GridObj := sgDaten.Objects[0, ARow] AS TGridObject;
    sHint := GridObj.GetVal(ACol);
    if (sgDaten.Hint <> sHint) then
    Begin
      Application.CancelHint();
      sgDaten.Hint := sHint;
    End;
  End;

end;
Jetzt wollte ich das aber ja, wie oben schon erkennbar, über eine zentrale Methode laufen lassen.
In den Tools hatte ich zuletzt dieses hier:
Delphi-Quellcode:
Procedure AutoGridHint(
  sgCurr : TStringGrid;
  const iCol : Integer;
  const iRow : Integer
);
var
  GridObj : IGridInfo;
  sHint : String;
Begin
  if (sgCurr.Objects[0, iRow] <> nil) AND
     (Supports(sgCurr.Objects[0, iRow], IGridInfo)) then
  Begin
    GridObj := IGridInfo(sgCurr.Objects[0, iRow]); // <- Nope
    sHint := GridObj.GetVal(iCol);
    if (sgCurr.Hint <> sHint) then
    Begin
      Application.CancelHint();
      sgCurr.Hint := sHint;
    End;
  End;
End;
Anstelle vom "IS"-Operator für Objekte soll man bei Interfaces offensichtlich
"Supports" aus den SysUtils nehmen.
Aber egal ob ich versuche den Cast zu erzwingen "IGridInfo(...)" oder
vorsichtig zu erfragen "X AS IGridInfo", der Compiler will das nicht.
Das unterliegende Objekt erbt ja das Interface, aber diese vorgehensweise ist
in Delphi wohl nicht vorgesehen.
Ich befürchte auch, dass "Supports" prüft, ob die Klasse "TObject" das Interface "IGridInfo" erbt
und nicht das Objekt, was tatsächlich gespeichert ist, denn die Funktion liefert mir "False".

"IS" und "AS", wie ich das von Klassen kenne läuft hier nicht, aber warum und wie löst man sowas dann?

Frage zusammengefasst also: Wenn ich eine Klasse A [TGridObject] erstelle,
welche vom Interface X [IGridInfo] erbt und ich ein
Objekt A1 dieser Klasse A erzeuge und in einer Variable V1 speichere,
welche vom Typ (Klasse) B [TObject] ist (wobei die Klasse A von Klasse B erbt),
wie kann ich auf die Funktionen des Interface X [IGridInfo] zugreifen, wenn ich
als Übergabe nur eine Variable V1 habe?

Oder vielleicht noch ein kurzes (unrealistisches!) Beispiel mit dem ersten Interface,
was ich in SysUtils gefunden habe (damit ist dann alles in einer Funktion):
Delphi-Quellcode:
Procedure Test();
var
  v1 : TSimpleRWSync;
  v2 : TObject;
  v3 : IReadWriteSync;
Begin
  // Erstelle Objekt vom Typ 1
  v1 := TSimpleRWSync.Create();

  // Speicher in Oberklasse:
  v2 := v1;

  // Mach irgendwas, um aus v2 das Interface zu bekommen:
  v3 := Irgendwas_IReadWriteSync(v2);

  // Arebite mit den Methoden des Interface:
  v3.BeginRead();
  v3.EndRead();
End;
Und wehe es kommt einer und sagt "mach doch einfach >v1.BeginRead<"!

Ich hoffe jemand hat da eine Idee zu.
Schonmal vielen Dank alleine für die Zeit für's lesen!

Liebe Grüße
Incocnito
  Mit Zitat antworten Zitat