Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Interface und "is" Operator


Spaceguide - Sa 14.10.06 12:20
Titel: Interface und "is" Operator
Hi,
wie kann ich herausfinden, ob ein Object, das ein Interface implementiert, von einem bestimmten Typ ist?

Beispiel:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type IMyInterface = class(IInterface)
     ...

     TMyControl = class(TCheckBox,IInterface);


var
 MyControl : TMyControl;
 MyInterface : IMyInterface;

...

 MyInterface := MyControl;

 if (MyInterface is??? TMyControl) then ...


Marco D. - Sa 14.10.06 12:29

Dürfte so mit is gehen. In einer anderen Anwendung schreibe ich:

Delphi-Quelltext
1:
if (Parent is TEdit) then ...                    

Funktioniert das bei dir nicht, oder was?


delfiphan - Sa 14.10.06 13:51

Du kannst lediglich mit support checken, ob eine Instanz ein Interface implementiert. Damit support funktioniert, musst du bei der Interface-Deklaration eine weltweit eindeutige GUID angeben (Ctrl+Shift+G).

Ausserdem fällt mir auf, dass dein Code so nicht viel Sinn ergibt. Statt IMyInterface = class(IInterface) war wohl eher IMyInterface = interface gemeint. Vielleicht hilft dir folgendes Beispiel auf die Sprünge:


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:
type
  IMyInterface = interface
   ['{0B4E2DA5-EFC8-49ED-8928-9ADD1E7D030D}']
  end;

  TMyObject = class(TInterfacedObject,IMyInterface)
    destructor Destroy; override;
  end;

destructor TMyObject.Destroy;
begin
  ShowMessage('Destroyed');
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  MyInterface: IMyInterface;
  MyObject: TMyObject;
begin
  MyObject := TMyObject.Create;
  MyInterface := MyObject;

  if MyObject is TMyObject then
   ShowMessage('MyObject is TMyObject');

  if Supports(MyInterface, IMyInterface) then
   ShowMessage('MyInterface supports IMyInterface');

  // automatische Freigabe
end;


Um rauszukriegen, ob MyInterface von TMyObject stammt könntest du die Funktion ClassType bei der Interface-Deklaration angeben (function ClassType: TClass;). Danach könntest du mit if MyInterface.ClassType = TMyObject die gewünschte Überprüfung vornehmen (ist aber nicht dasselbe wie "is", man testet hier auf Gleichheit). Ich bezweifle aber, dass sowas nötig ist, um das zu erreichen, was du erreichen willst. Ich habe noch nie einen Code gesehen, wo man ein Interface auf eine Klasse testet... Das ist IMHO keine saubere OOP.


Spaceguide - Sa 14.10.06 15:23

Ist jetzt zwar nicht wirklich schön, aber ich kann dem Interface eine Funktion GetObject : TObject verpassen, die Implementierungen geben dann einfach Result := self zurück, was ich mit "is" überprüfen kann. Mal sehen ob das funktioniert.


delfiphan - Sa 14.10.06 17:05

Das übliche Konzept bei den Interfaces ist, dass man verschiedene Teile des Programmes voneinander abkoppeln und trennen kann. Um das sauber realisieren zu können braucht man dazu entweder Mehrfachvererbungen oder eben die Interfaces, welche auch Mehrfachvererbungen zulassen, jedoch ohne Implementation. Damit die Entkopplung Sinn macht sollte ein Teil deines Programmes nur das entsprechende Interface kennen müssen. Wenn du GetClass mit irgendwelchen if und is Statements verwenden musst hast du vermutlich ein schlechtes Programmdesign gewählt.

Ja, es wird funktionieren. Aber ich würde davon abraten.


Spaceguide - So 15.10.06 20:24

Recht hast du, es ist unschön. Ich brauche das Interface für eine Art Mehrfachvererbung. Alle Klassen, die das Interface implementieren, sind irgendwelche von TControl abgeleiteten Controls (TEdit, TMemo, TSpinEdit, etc.), welche ich in meine Persistenzschicht einbetten will. Dafür stellt das Interface Methoden wie GetValue und SetValue bereit. Das passt auch alles sehr gut, nur die TRadioButton-Klasse tanzt etwas aus dem Design, weil ich damit mehrere Radiobuttons an einen einzigen persistenten Wert (den Index des Radiobuttons mit Checked=True) binden muss. TRadioButtonGroup will ich nicht verwenden, weil zu unflexibel. Eventuell kann ich ja eine nichtvisuelle Komponente erstellen, welche die Radiobuttons als Liste führt, mal schaun. Danke für deine Tips.


delfiphan - Mo 16.10.06 10:43

... was man ab und zu macht ist die Interfaces als Marker zu verwenden (ein leeres Interface, so wie beim Beispiel oben).

Man könnte dann if (myinterface.getclass is tmyclass1) or (myinterface.getclass is tmyclass2) ersetzen durch if supports(myinterface, mymarker). Man müsste bei tmyclass1 und tmyclass2 lediglich mymarker in die Vererbungsliste eintragen.

Bei der neusten Java-Version gibt es hierfür extra Attributes, aber in früheren Versionen wurden ganz offiziell leere Interfaces zum Markieren "missbraucht".