Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Cast class to interface


delphijanka - Fr 17.09.10 16:13
Titel: Cast class to interface
Hallo. Habe ein Problem. Gegeben:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
//Interfaces

type
  ILaufer = Interface
    procedure laufe;
  end;

type
  ISpringer = Interface
    procedure springe;
  end;

//Klassen

type
  TTier = class(TInterfacedObject)
    procedure esse;
  end;

type
  TElefant = class(TTier , ILaufer)
    procedure laufe;
  end;

type
  TKenguru = class(TTier, TSpringer)
    procedure springe;
  end;

type
  TLeopard = class(TTier, TLaufer, TSpringer)
    procedure laufe;
    procedure springe;
  end;


So verwalte ich meine Tiere:


Delphi-Quelltext
1:
2:
var
  Tiere = array of TTier;


Nun möchte ich die Fähigkeiten der Tiere ausnutzen, was in Pseudo so aussieht:

wenn Tier[i] ist ein ISpringer
dann (Tier[i] als ISpringer).springe;


Ich weiß allerdings nicht, wie es geht. Kann mir einer helfen? Danke.

Moderiert von user profile iconNarses: Code- durch Delphi-Tags ersetzt


Martok - Fr 17.09.10 16:40

user profile icondelphijanka hat folgendes geschrieben Zum zitierten Posting springen:

Nun möchte ich die Fähigkeiten der Tiere ausnutzen, was in Pseudo so aussieht:

wenn Tier[i] ist ein ISpringer
dann (Tier[i] als ISpringer).springe;

Ich weiß allerdings nicht, wie es geht.

Doch, tust du. Steht alles schon da, wenn du das 1:1 nach Englisch (oder Pascal :D ) übersetzt:


Delphi-Quelltext
1:
2:
if Tier[i] is ISpringer then
  (Tier[i] as ISpringer).springe;


Oder, schöner (und ggf. einfacher, wenn du mehrere Methoden aufrufen willst):

Delphi-Quelltext
1:
2:
3:
4:
var Springtier: ISpringer;
{...}
  if Tier[i].QueryInterface(ISpringer, Springtier)=S_OK then 
    SpringTier.springe;


delphijanka - Fr 17.09.10 16:51

@Martok

Lösung (a):

- geht schon mal nicht: "Operator not applicable to this operand type"

Lösung (b):

- die Methode QueryInterface ist nicht bekannt.
- Was schreibe ich an der {...} - Stelle? Wie erzeuge ich ein Objekt von ISpringer?


Martok - Fr 17.09.10 17:12

(a) Könnte sogar stimmen, damit man sowas tun kann, braucht das Interface eine GUID. Steht irgendwo in der Delphi-Hilfe welche Tastenkombination das war und was genau man damit tun soll.

(b) Faszinierend. Das ist in TInterfacedObject wirklich protected. Dann eben anders und noch schöner :P


Delphi-Quelltext
1:
2:
3:
4:
var Springtier: ISpringer;
{...}
  if Supports(Tier[i], ISpringer, Springtier) then 
    SpringTier.springe;


Anstelle der Auslassung dürfte wohl mindestens ein begin und ein for kommen. Aber das musst du schon wissen...


delphijanka - Fr 17.09.10 17:15

@Martok

Wie ich erstelle ich ein Objekt vom Interface-Typ. In diesem Fall vom ISpringer?


mkinzler - Fr 17.09.10 19:05

Man kann kleine Instanzen von Interfacen erzeugen. Du musst eine Klasse von dem Interface ableiten und diese dann instantiieren


delphijanka - Mo 20.09.10 00:55

@Martok und mkinzler:

Die Prozeduren QueryInterface oder Supports erwarten Interface-Parameter vom Typ TGuid, wogegen ISpringer nicht von diesem Typ ist.
Daher kommt die Fehlermeldung. Gibt es denn keine Lösung für mein Problem in Delphi? Muss ich unbedingt die Deklaration mit TGuid machen?
(Warum ist das in Delphi nur so kompliziert?... :( ). Wäre sehr dankbar für eine funktionierende Lösung.


Martok - Mo 20.09.10 01:11

user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
(a) Könnte sogar stimmen, damit man sowas tun kann, braucht das Interface eine GUID. Steht irgendwo in der Delphi-Hilfe welche Tastenkombination das war und was genau man damit tun soll.

Hilfeseiten dazu: "Schnittstellentypen" (kriegt man bei F1 auf "interface") und "Identifikation einer Schnittstelle" von da aus.

Sobald du das Interface identifizierbar gemacht hast durch Vergabe eine GUID kannst du es anstelle von GUIDs verwenden.


delfiphan - Mo 20.09.10 12:22

Wenn du innerhalb der Hierarchie bleibst und nicht irgendwie quer oder hoch castest, dann kannst du einfach ILaufer(...) schreiben und brauchst auch keine GUIDs.

Also von TTier auf ISpringer brauchst du GUIDs, von TLeopard nicht. Hängt ein wenig vom Anwendungsfall ab.


delphijanka - Fr 24.09.10 15:08

zu der Lösung, wie Martok sie vorgeschlagen hat, eine kleine Abwandlung, die
ich in meinem Prog benutze:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var
  SpringTier: IPDFErzeuger;
  Unk: IUnknown;
begin
if Tier[i].GetInterface(IUnknown, Unk) and Supports(Unk, ISpringer, SpringTier) then 
  SpringTier.springe;

Und dabei habe ich ein Problem: das Objekt Tier[i] wird nach diesem Code automatisch freigegeben. Liegt es an Referenzzählung? Wie kann ich erreichen, dass es nicht freigegeben wird (ich muss Tier[i] ja weiter benutzen...)


bummi - Fr 24.09.10 15:42

Hat mich auch schon viele Nerven gekostet...
nimm TinterFacedPersistent, dann tickt es so wie wir es wollen.


delphijanka - So 26.09.10 17:51

@bummi

Bei der Nutzung von TInterfacedPersistent kommt der Fehler, der Bezeichner sei undefiniert.
(benutze zur Zeit Delphi 5)