Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Umgang mit Interfaces (https://www.delphipraxis.net/177920-umgang-mit-interfaces.html)

Whookie 5. Dez 2013 16:04


Umgang mit Interfaces
 
Hallo!
Ich habe diesmal eine grundsätzliche Frage zum Umgang mit Interfaces, weil ich gerade eine entsprechendes Projekt erstelle.

Gedacht habe ich mir das so: Es ibt eine Fülle von "kleinen" Interfaces die selber auch eine entsprechende Hierarchie haben (Details wie CC und GUID weggelassen):

Delphi-Quellcode:
IBase=Interface(IUnknown)
  function GetCanSave: Boolean;
end;

IValue=Interface(IBase)
  function GetIsNaN: Boolean;
End;

IInteger=Interface(IValue)
  function GetValue: Integer;
  procedure SetValue(const AValue: Integer);
End;

ICompare=Interface(IBase)
  function Compare: Boolean;
End;

ISomeThing=Interface(IBase)
  function DoSomeThing: Integer;
End;
Aus den Intefaces baue ich mir dann verschiedene Klassen wie zum Beispiel (ohne Code):

Delphi-Quellcode:
TMyClasses = Class(TInterfacedObject)
End;

TMyBase = Class(TMyClasses, IBase)
End;

TMyInteger = Class(TMyBase, IInteger)
End;

TMyCompare = Class(TMyBase, ICompare)
End;

TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing)
  fAValue: TMyClasses;
End;
Alle diese Klassen werden nun in einer List verwaltet:

Delphi-Quellcode:
TListData =Class
  fMy: TMyClasses;
  fName: String;
  fHash: Cardinal;
End;
TMyList = Class
  fList: TObjectList<TListData>
End;
Das eigentliche Problem habe ich nun aber beim Arbeiten mit der TMyList. Sie ist mit meinen (polymorphen) Objekten befüllt und nun geht es darum möglichst elegant auf die einzelnen Methoden zugreifen zu können.


Was funktioniert ist natürlich alle möglichen Klassen abzufragen und zu casten:

Delphi-Quellcode:
  If fList[i].fMy Is TMyDoSomething Then
  begin
    TMyDoSomething(fList[i].fMy]).SetValue(1);
    TMyDoSomething(fList[i].fMy]).DoSomething;
    If TMyDoSomething(fList[i].fMy]).fAValue Is TMyInteger Then
    begin
      TMyInteger(TMyDoSomething(fList[i].fMy]).fAValue).SetValue(2);
    end;
  end
  else if (fList[i].fMy Is TMyInteger) Then
  begin
    TMyInteger(fList[i].fMy).SetValue(1);
  end;
Das ist nicht besonders schön vor allem auch weil ich dann gerne SetValue unabhängig von der Klasse aufrufen möchte.

Ein Zugriff über das Interface führt aber immer zu Schutzverletzungen (wegen der Referenzzählung) und wird in einem komplexeren System wohl schnell unüberschaubar:

Delphi-Quellcode:
  If Supports(fList[i].fMy, IID_IInteger) then
  begin
    (fList[i].fMy As IInteger).SetValue(1);
  end;
Ich habe auch probiert die Referenzzählung "positiv" zu beeinflussen (_AddRef/_Release selber aufrufen) aber der Verwaltungsaufwand ist entsprechend groß und fehleranfällig wird das ganze auch.

Daher natürlich die Frage, wie kann man sowas besser organisieren? Ich dachte auch schon daran eine funktion in TMyClasses einzubauen die ein Interface liefert:

Delphi-Quellcode:
  If fList[i].fMy.GetInterface(IID_xx, AIntegerIntf) Then
    AIntegerIntf.SetValue(1);
aber da schlägt die Referenzzählung auch zu (und kapselt eigentlich nur den Supports-Aufruf).

Jedes einzelne Interface in eine Klasse zu packen und dann mit implements in den TMyXXX-Klassen als Delegate einzuhängen ist ein großer Aufwand und nimmt die Flexibilität der Interfaces wieder heraus.

Was wäre der beste Ansatz sowas möglichst transparent und sauber zu lösen?

himitsu 5. Dez 2013 16:27

AW: Umgang mit Interfaces
 
Wenn du mit Interfaces arbeiten willst, dann verwende die Objekte auch durchgängig nur als Interfaces, dann wirst du auch keine Probleme mit der Referenzzählöung bekommen.
Delphi-Quellcode:
fMy: IMyBase;




Oder du verwendest Klassen "ohne" Referenzzählung und regelst die Speicherverwaltung nur über das Objekt. (.Free)

Mir war so, als wenn Delphi schon eine Interface-Klasse ohne Referenzzählung besitzt (dachte die liegt neben TInterfacedObject), aber ich finde sie nicht.
(TPersistent und TComponent gäbe es noch, aber die sind wohl schon zu aufgebläht)

Notfalls einfach TInterfacedObject ableiten, bzw. in deiner Basisklasse das entsprechend überschreiben.
Delphi-Quellcode:
function TComponent._AddRef: Integer;
begin
  Result := -1;
end;

function TComponent._Release: Integer;
begin
  Result := -1;
end;


Wozu existiert eigentlich TMyClasses, wo doch erst bei TMyBase/IMyBase die implemtierten Funktionen anfangen?
Und die Klassen TMyInteger, sowie TMyCompare scheinen auch erstmal keine Funktionoalität zu beinhalten.

Uwe Raabe 5. Dez 2013 16:33

AW: Umgang mit Interfaces
 
Zitat:

Zitat von himitsu (Beitrag 1238668)
Mir war so, als wenn Delphi schon eine Interface-Klasse ohne Referenzzählung besitzt (dachte die liegt neben TInterfacedObject), aber ich finde sie nicht.

Delphi-Quellcode:
TInterfacedPersistent
in System.Classes

Der schöne Günther 5. Dez 2013 16:33

AW: Umgang mit Interfaces
 
Ich habe es jetzt nur überflogen, aber eine Standard-Implementation von durch Interfaces referenzierten Objekten ohne ARC ist
Delphi-Quellcode:
TSingletonImplementation
. Nicht nur der Name ist verwirrend, sondern auch der Namensraum. Es steckt in
Delphi-Quellcode:
System.Generics.Defaults
. Merkwürdige Welt.

jaenicke 5. Dez 2013 16:39

AW: Umgang mit Interfaces
 
So nach dem Motto würde ich das machen, ungetestet:
Delphi-Quellcode:
TMyList = class
  private
    var
      FList: TList<IListData>;
  public
    function Get<T: IInterface>(const AIndex: Integer): T;
  end;

...
function TMyList.Get<T>(const AIndex: Integer): T;
begin
  if not Supports(FList[AIndex], Result) then
    Result := nil;

himitsu 5. Dez 2013 17:06

AW: Umgang mit Interfaces
 
Zitat:

TInterfacedPersistent
Hab da nochmal reingesehn ... Joar, diese Basisklasse sollte gehn. (war nur durch den Owner etwas verwirrt, welcher da für die Referenzzählung verwendet wird)


System.Generics.Defaults gibt es bei mir nicht und wird es auch nicht geben. (Delphi wird langsam zu teuer)
[edit] Mist, gibt es doch, aber nur daheim und hier im XE noch nicht.

DeddyH 6. Dez 2013 07:29

AW: Umgang mit Interfaces
 
Generics.Defaults gibt es auch schon in XE (vermutlich bereits ab 2009, aber das habe ich nicht installiert).

himitsu 6. Dez 2013 08:43

AW: Umgang mit Interfaces
 
Ohhh mann, jetzt fällt es mir wie schuppen von den Augen .... noch ein Namespace davor. :wall:
Nur voll bescheuert, daß die Codevervollständigung nur nach Wortanfängen sucht und man jetzt nix mehr findet.

z.B. "SysUtils" > Strg+Enter > Namespace kopieren > Strg+F4 und reinkopieren ist doch keine Lösung, bis man sich den Mist endlich gemerkt hat.

TiGü 6. Dez 2013 08:51

AW: Umgang mit Interfaces
 
Zitat:

Zitat von DeddyH (Beitrag 1238715)
Generics.Defaults gibt es auch schon in XE (vermutlich bereits ab 2009, aber das habe ich nicht installiert).

Ja, gibt es schon in 2009!

Die Lösung von jaenicke mit einer generischen TList<T> ist das auf jeden Fall besser als eine TObjectList.
Für deine Zwecke wäre sogar ein generisches TDictionary besser, da du ja irgendwie auf die Objekte per Namen zugreifen willst (siehe TListData.fName).

Das ständige Rumgecaste mit unlesbaren Code (
Delphi-Quellcode:
TMyInteger(TMyDoSomething(fList[i].fMy]).fAValue).SetValue(2);
)
führt nur zu Problemen und in drei Monaten verstehst du es selber nicht mehr.
Die Referenzzählung selber verursacht die Schutzverletzung nicht, sondern der fehlerhafte Umgang mit Interfaceobjekten.

Kleiner Tipp:
Es ist möglich in Interfaces mit propertys zu arbeiten.
Find ich angenehmer beim Programmieren:

Delphi-Quellcode:
IInteger=Interface(IValue)
  function GetValue: Integer;
  procedure SetValue(const AValue: Integer);
  property Value : interger
    read GetValue
    write SetValue;
end;

TiGü 6. Dez 2013 08:58

AW: Umgang mit Interfaces
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von himitsu (Beitrag 1238724)
Nur voll bescheuert, daß die Codevervollständigung nur nach Wortanfängen sucht und man jetzt nix mehr findet.

Versuche mal den Code Input Helper vom CnPack. Der kann auch "mittendrin".
Hier schreibe ich bspw. in den uses nur "generic" und er bietet mir gleich die beiden passenden Units an.

Der schöne Günther 6. Dez 2013 08:58

AW: Umgang mit Interfaces
 
Zitat:

Zitat von himitsu (Beitrag 1238724)
Nur voll bescheuert, daß die Codevervollständigung nur nach Wortanfängen sucht und man jetzt nix mehr findet.

Nicht nur das, auch das (sowieso nur sporadisch funktionierende) "Inkludiere mir die passende Unit" mittels Strg+Shift+A. Muss man halt immer auswendig lernen, in welchen Units die jeweiligen Klassen so stecken. Oder man geht sich erstmal einen Kaffee holen während die dexplore-Hilfe bzw. die superschnellen Embarcadero-Server laden (und nichts finden).

Mutet zwar im Jahr 2013 etwas komsich an, aber irgendwie muss man seinen Kopf ja auch trainieren :spin:

TiGü 6. Dez 2013 09:12

AW: Umgang mit Interfaces
 
Zitat:

Zitat von TiGü (Beitrag 1238727)
Für deine Zwecke wäre sogar ein generisches TDictionary besser, da du ja irgendwie auf die Objekte per Namen zugreifen willst (siehe TListData.fName).

Mal ein Beispiel, als Keys des Dictonarys (auch Map genannt) kann man vielleicht auch die GUID der Interfaces nehmen:

Delphi-Quellcode:
unit Unit2;

interface

uses
  Winapi.Windows,
  Winapi.Messages,
  System.SysUtils,
  System.Variants,
  System.Classes,
  Vcl.Graphics,
  Vcl.Controls,
  Vcl.Forms,
  Vcl.Dialogs,
  System.Generics.Collections,
  Vcl.StdCtrls;

type
  IBase = Interface(IUnknown)
    function GetCanSave : Boolean;
  end;

  IValue = Interface(IBase)
    function GetIsNaN : Boolean;
  End;

  IInteger = Interface(IValue)
    function GetValue : Integer;
    procedure SetValue(const AValue : Integer);
  End;

  ICompare = Interface(IBase)
    function Compare : Boolean;
  End;

  ISomeThing = Interface(IBase)
    function DoSomeThing : Integer;
  End;

  TMyBase = Class(TInterfacedObject, IBase)
  public
    function GetCanSave : Boolean;
  End;

  TMyInteger = Class(TMyBase, IInteger)
  public
    function GetValue : Integer;
    procedure SetValue(const AValue : Integer);
    function GetIsNaN : Boolean;
  End;

  TMyCompare = Class(TMyBase, ICompare)
  public
    function Compare : Boolean;
  End;

  TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing)
  public
    function DoSomeThing : Integer;
    function GetValue : Integer;
    procedure SetValue(const AValue : Integer);
    function GetIsNaN : Boolean;
  End;

  TForm2 = class(TForm)
    Button1 : TButton;
    procedure FormCreate(Sender : TObject);
    procedure Button1Click(Sender : TObject);
  private
    { Private declarations }
    fList : TDictionary<string, IBase>;
  public
    { Public declarations }
  end;

var
  Form2 : TForm2;

implementation

{$R *.dfm}

{ TMyCompare }

function TMyCompare.Compare : Boolean;
begin

end;

{ TMyInteger }

function TMyInteger.GetIsNaN : Boolean;
begin
  //
end;

function TMyInteger.GetValue : Integer;
begin
  //
end;

procedure TMyInteger.SetValue(const AValue : Integer);
begin
  //
end;

{ TMyDoSomething }

function TMyDoSomething.DoSomeThing : Integer;
begin
  //
end;

function TMyDoSomething.GetIsNaN : Boolean;
begin
  //
end;

function TMyDoSomething.GetValue : Integer;
begin
  //
end;

procedure TMyDoSomething.SetValue(const AValue : Integer);
begin
  //
end;

{ TMyBase }

function TMyBase.GetCanSave : Boolean;
begin
  //
end;

procedure TForm2.FormCreate(Sender : TObject);
begin
  fList := TDictionary<string, IBase>.Create;

  fList.Add(TMyInteger.ClassName , TMyInteger.Create);
  fList.Add(TMyCompare.ClassName , TMyCompare.Create);
  fList.Add(TMyDoSomething.ClassName , TMyDoSomething.Create);
end;

procedure TForm2.Button1Click(Sender : TObject);
var
  MyObject : IBase;
begin
  if fList.TryGetValue(TMyDoSomething.ClassName, MyObject) then
  begin
    IInteger(MyObject).SetValue(1);
    ISomeThing(MyObject).DoSomeThing;
  end;

  if fList.TryGetValue(TMyInteger.ClassName, MyObject) then
  begin
    IInteger(MyObject).SetValue(2);
  end;

  if fList.TryGetValue(TMyCompare.ClassName, MyObject) then
  begin
    ICompare(MyObject).Compare;
  end;
end;

end.

himitsu 6. Dez 2013 10:18

AW: Umgang mit Interfaces
 
Zitat:

Zitat von TiGü (Beitrag 1238727)
Die Referenzzählung selber verursacht die Schutzverletzung nicht, sondern der fehlerhafte Umgang mit Interfaceobjekten.

Jupp, die funktioniert so, wie sie soll.

Aber, sobald von einem "referenzzählenden" Objekt eine Interface-Referenz "erzeugt" wurde, dann übernimmt die Referenzzählung die Kontrolle über die Speicherverwaltung.
Sobald dann die letzte Referenz weg ist, wird das Objekt freigegeben und dein Objektzeiger wird ungültig.
Ohne Interfacereferenzen bleibt die Speicherfreigabe dem .Free überlassen.

Darum war auch mein Tipp:
Wenn mit "referenzzählenden" Interfaces gearbeitet werden soll, dann möglichst durchgehend nur noch mit Interface-Referenzen arbeiten
und wenn Objekt-Referenzen nötig sind, dann muß man dort eben aufpassen.

Uwe Raabe 6. Dez 2013 13:20

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1238730)
Zitat:

Zitat von himitsu (Beitrag 1238724)
Nur voll bescheuert, daß die Codevervollständigung nur nach Wortanfängen sucht und man jetzt nix mehr findet.

Nicht nur das, auch das (sowieso nur sporadisch funktionierende) "Inkludiere mir die passende Unit" mittels Strg+Shift+A. Muss man halt immer auswendig lernen, in welchen Units die jeweiligen Klassen so stecken. Oder man geht sich erstmal einen Kaffee holen während die dexplore-Hilfe bzw. die superschnellen Embarcadero-Server laden (und nichts finden).

Mutet zwar im Jahr 2013 etwas komsich an, aber irgendwie muss man seinen Kopf ja auch trainieren :spin:

Oder man verwendet gleich den ModelMaker CodeExplorer...

Der schöne Günther 6. Dez 2013 13:43

AW: Umgang mit Interfaces
 
Ja ich weiß - Das Teil ist sein Geld wirklich mehr als wert.

Nach dem letzten Einkauf (TChart Pro) will ich vorerst nicht schon wieder laut nach weiteren Investitionen schreien ;-)

Vorerst

Whookie 9. Dez 2013 23:11

AW: Umgang mit Interfaces
 
Zitat:

Zitat von jaenicke (Beitrag 1238672)
So nach dem Motto würde ich das machen, ungetestet:
Delphi-Quellcode:
TMyList = class
  private
    var
      FList: TList<IListData>;
  public
    function Get<T: IInterface>(const AIndex: Integer): T;
  end;

...
function TMyList.Get<T>(const AIndex: Integer): T;
begin
  if not Supports(FList[AIndex], Result) then
    Result := nil;

Ich fürchte mein Verständnis für Interfaces und Generics reicht nicht aus um das Beispiel nachzuvollziehen... IListData als Typ der Liste hätte ich noch als Tippfehler gesehen sodass daraus FList:TList<TListData>; wird?

Die Get-Funktion wird dann zu:
Delphi-Quellcode:
function TMyList.Get<T>(const AIndex: Integer): T;
begin
  if not Supports(FList[AIndex].fMy, Result) then //<- Error
    Result := nil;
end;
wobei das Resultat auf IInterface-Support eingeschränkt wird(?). Da man aber Supports nicht ohne eine IID_xxx aufzurufen kann, muss man hier wieder ein konkretes Interface angeben. Also etwa:

Delphi-Quellcode:
function TMyList.Get<T>(const AIndex: Integer): T;
begin
  if not Supports(FList[AIndex].fMy, IID_BASE, Result) then
    Result := nil;  
end;
Womit eigentlich nur mehr das IBase-Interface von der Funktion zu haben wäre? Ausprobieren konnte ich das ganze nicht, denn obwohl sich der Code der Liste compilieren lässt, bekomme ich dann bei einem Zugriffsversuch vom Compiler die Meldung: "E2531 Methode 'Get' erfordert explizite Typargumente", was eventuell daran liegt, dass ich keinen korrekten Aufruf hinbekommen habe..

Code:
  i: IBase;

  i := fMyList.Get(0);
  i := fMyList.Get(0) As IBase;
  ...

Wünschen würde ich mir ja eher sowas wie:
Delphi-Quellcode:
Var
  iSom: ISomeThing;
  iInt: IInteger;
begin
  for i := 0 To fMyList.Count-1 Do
  begin
    if fMyList.Get(i, iInt) Then
      iInt.SetValue(Random(100));
    if fMyList.Get(0, iSom) Then
      iSom.DoSomething;
    ..
Unabhängig von der korrekten Implementierung frage ich mich auch noch wie die Performanz hier aussieht. Es werden ja ständig Interfaces über Strings gesucht?

TiGü 10. Dez 2013 09:03

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Whookie (Beitrag 1239230)
Da man aber Supports nicht ohne eine IID_xxx aufzurufen kann, muss man hier wieder ein konkretes Interface angeben. Also etwa:

Delphi-Quellcode:
function TMyList.Get<T>(const AIndex: Integer): T;
begin
  if not Supports(FList[AIndex].fMy, IID_BASE, Result) then
    Result := nil;  
end;

Das ist so nicht richtig, siehe Doku:
Delphi-Quellcode:
function Supports(const Instance: IInterface; const IID: TGUID; out Intf): Boolean;
function Supports(const Instance: TObject; const IID: TGUID; out Intf): Boolean;
function Supports(const Instance: IInterface; const IID: TGUID): Boolean;
function Supports(const Instance: TObject; const IID: TGUID): Boolean;
function Supports(const AClass: TClass; const IID: TGUID): Boolean;
Wobei IID hier auch direkt ein Interface-Typ sein kann (IMyFunnyInterface, IInteger, IDoSomeThing).

Codebeispiel anhand deines Falls:

Delphi-Quellcode:
type
  TMyDoSomething = Class(TMyCompare, IInteger, ISomeThing)
  ...
  end;

var
  MySomeThingObject : ISomeThing;
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
end;

Whookie 11. Dez 2013 20:53

AW: Umgang mit Interfaces
 
Zitat:

Zitat von TiGü (Beitrag 1239253)
Delphi-Quellcode:
  ...
var
  MySomeThingObject : ISomeThing;
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
end;

... und laut Doku ist das Objekt damit auch zerstört und damit kann man diese Variante nicht verwenden...

Edit: Habe nicht erwähnt das ich meine Klassen nicht als Interface speichern kann (ISomeThing muss zu TSomeThing werden), weil ich eben eine "Basisliste" mit allen Klassen habe. Diese stehen untereinander in verschiedenen Beziehungen und pflegen daher eigene TNodeList-en um eine Referenz auf diese benötigten Instanzen zu haben. Durch diese Verlinkung schnellt der _RefCount bei verwendung von Interfaces aber hoch und am Programmende, wenn die "Basisliste"-alles frei gibt, bleibt ein riesiges Memoryleek über.

jaenicke 11. Dez 2013 22:19

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Whookie (Beitrag 1239470)
Edit: Habe nicht erwähnt das ich meine Klassen nicht als Interface speichern kann (ISomeThing muss zu TSomeThing werden), weil ich eben eine "Basisliste" mit allen Klassen habe.

Das ist dann keine gute Architektur, lässt sich aber bei Einführung von Interfaces in alte Quelltexte manchmal nicht vermeiden. In neuen Quelltexten ist das allerdings ein No-Go.

Dann solltest du die Referenzzählung auf jeden Fall in allen Objekten deaktivieren. Es macht keinerlei Sinn die zu nutzen, wenn du nicht durchgängig mit Interfaces arbeitest.

Das haben wir als Notlösung bei der Umstellung in alten Quelltexten vorübergehend auch gemacht, sind aber mittlerweile so weit, dass wir es größtenteils richtig, sprich nur mit Interfaces, umgesetzt haben. Das ist bei Verwendung von Interfaces ja die einzige sinnvolle Variante.

JamesTKirk 12. Dez 2013 06:27

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Whookie (Beitrag 1239470)
Habe nicht erwähnt das ich meine Klassen nicht als Interface speichern kann (ISomeThing muss zu TSomeThing werden), weil ich eben eine "Basisliste" mit allen Klassen habe.

Du könntest mal probieren, ob's richtig funktioniert, wenn du bei deinen Klassen explizit
Delphi-Quellcode:
_AddRef
aufrufst, bevor du es in deine Liste speicherst. Damit sagst du der Referenzzählung ja, dass da noch eine Referenz ist. Bevor du die Klasse dann freigibst musst du diese Referenz dann wieder mit
Delphi-Quellcode:
_Release
lösen und wenn keine andere Referenz auf eines der Interfaces der Klasse vorhanden ist, sollte sie auch gleich automatisch freigegeben werden. :?

Gruß,
Sven

Stevie 12. Dez 2013 07:02

AW: Umgang mit Interfaces
 
Ändere einfach in deinem Ausgangspost fMy von TMyClasses auf IInterface und benutz dann die Version mit Supports (GUIDs auf den Interfaces nicht vergessen) - dann zerbröselt es dir deine Referenzzählung nicht.

Sir Rufo 12. Dez 2013 07:22

AW: Umgang mit Interfaces
 
Oder sogar doppelt ... falls die Basis-Instanz als Klasse benötigt wird
Delphi-Quellcode:
TListData = class
private
  fAsIntf : IInterface;
  fAsObj: TMyClasses;
  fName: String;
  fHash: Cardinal;
public
  property AsIntf : IInterface read fAsIntf;
  property AsObj : TMyClasses;
  property Name : string read fName;
  property Hash : Cardinal read fHash;
End;
Und beim Destroy von TListData einfach mal nicht die Objekt-Instanz befreien :)

TiGü 12. Dez 2013 07:47

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Whookie (Beitrag 1239470)
Zitat:

Zitat von TiGü (Beitrag 1239253)
Delphi-Quellcode:
var
  MySomeThingObject : ISomeThing;
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
end;

... und laut Doku ist das Objekt damit auch zerstört und damit kann man diese Variante nicht verwenden...

Lesen und verstehen der Doku sind zwei Paar Schuhe!
Was unter der Überschrift "Warnung" als kryptischer 53 Wörter Satz steht, heißt nicht weiter als:
"Pass ja auf wenn du Interface- mit Objektreferenzen mischt, dass kann nämlich ganz schön in die Hose gehen wenn du mit Supports arbeitest."
Auch hier gilt: Nicht die Supports-Funktion ist schlecht oder fehlerhaft, sondern der Programmierer hat einen Fehler gemacht!

Um beim Beispiel zu bleiben, das hier funktioniert so wie es soll:
Delphi-Quellcode:
var
  MySomeThingObject : ISomeThing; // <--- das ist so richtig
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
  MySomeThingObject.DoSomeThing;
end;
Während das hier schief geht, wenn in DoSomeThing auf objekteigene Werte zugegriffen wird, denn die sind dann schon freigeben und genullt:
Delphi-Quellcode:
var
  MySomeThingObject : TMyDoSomething; // <--- da ist die entscheidene Stelle, Fehler!!!
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
  MySomeThingObject.DoSomeThing;
end;

Patito 12. Dez 2013 08:46

AW: Umgang mit Interfaces
 
Zitat:

Zitat von TiGü (Beitrag 1239483)
Auch hier gilt: Nicht die Supports-Funktion ist schlecht oder fehlerhaft, sondern der Programmierer hat einen Fehler gemacht!

Delphi-Quellcode:
var
  MySomeThingObject : ISomeThing; // <--- das ist so richtig
Während das hier schief geht, wenn in DoSomeThing auf objekteigene Werte zugegriffen wird, denn die sind dann schon freigeben und genullt:
Delphi-Quellcode:
var
  MySomeThingObject : TMyDoSomething; // <--- da ist die entscheidene Stelle, Fehler!!!

Naja. Der Fehler liegt doch etwas tiefer. Letztenendes ist es der Fehler der Compiler-Entwickler, die die Interfaces bei Delphi vermasselt haben. Diese Idee vom 'Interfaces nie als Objekte verwenden' stammt doch aus der Zeit, als man noch geglaubt hat, dass alle Objekt in 5 Monaten nur noch ActiveX-Objekt sein werden, (und dass das mehr oder weniger der heilige Gral ist). Im Licht der täglichen Praxis und im Angesicht von Design-Patterns erscheint diese Idee ziemlich absurd.

Eine solidere Lösung wäre einen Compiler zu verwenden, der besser ist (z.B. FreePascal mit "Corba-Interfaces").
Wenn man bei Delphi bleibt muss man eben den unsichtbaren Refenz-Zähler Kram so gut es geht aushebeln und höllisch aufpassen.

P.S: Zur Laufzeit mit Supports zu arbeiten ist etwas, was man so gut es geht vermeiden sollte. Eine Haupterrungenschaft der Objektorientierung war, dass man statt mit schwammigen Pointern jetzt mit feste Typen arbeiten kann. Wenn man jetzt wieder anfängt zur Laufzeit Pointer herumcasten ist das irgendwie sehr Retro.

jaenicke 12. Dez 2013 09:06

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Patito (Beitrag 1239490)
Eine solidere Lösung wäre einen Compiler zu verwenden, der besser ist (z.B. FreePascal mit "Corba-Interfaces").
Wenn man bei Delphi bleibt muss man eben den unsichtbaren Refenz-Zähler Kram so gut es geht aushebeln und höllisch aufpassen.

Richtig nutzen wäre eine Alternative... sprich nur Interfaces verwenden oder keine.

Zitat:

Zitat von Patito (Beitrag 1239490)
P.S: Zur Laufzeit mit Supports zu arbeiten ist etwas, was man so gut es geht vermeiden sollte. Eine Haupterrungenschaft der Objektorientierung war, dass man statt mit schwammigen Pointern jetzt mit feste Typen arbeiten kann. Wenn man jetzt wieder anfängt zur Laufzeit Pointer herumcasten ist das irgendwie sehr Retro.

Eben das passiert ja nicht. Supports prüft ja, ob das Interface unterstützt wird, arbeitet also ähnlich wie as bei Objekten. Deshalb ist das ja gerade typsicher. Im Gegensatz zu einem harten Cast bei Objekten.

TiGü 12. Dez 2013 09:24

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Patito (Beitrag 1239490)
Eine solidere Lösung wäre einen Compiler zu verwenden, der besser ist (z.B. FreePascal mit "Corba-Interfaces").
Wenn man bei Delphi bleibt muss man eben den unsichtbaren Refenz-Zähler Kram so gut es geht aushebeln und höllisch aufpassen.

Wer meint es besser zu wissen bzw. sein Anwendungsfall es unbedingt erfordert, muss ja nicht von TInterfacedObject ableiten, sondern kann selber die IInterface-Methoden implementieren bzw. wie schon von Günther angesprochen mit System.Generics.Defaults.TSingletonImplementation arbeiten.

Die "automatische" Referenzverwaltung von Inferface-Objekten ist in meinen Augen sehr bequem und ermöglicht ein sehr komfortables modernes programmieren.
Ähnliches wurde ja im Next-Gen-Compiler für mobile Platformen von Emba umgesetzt.

Zitat:

Zitat von Patito (Beitrag 1239490)
P.S: Zur Laufzeit mit Supports zu arbeiten ist etwas, was man so gut es geht vermeiden sollte. Eine Haupterrungenschaft der Objektorientierung war, dass man statt mit schwammigen Pointern jetzt mit feste Typen arbeiten kann. Wenn man jetzt wieder anfängt zur Laufzeit Pointer herumcasten ist das irgendwie sehr Retro.

Das ist so auch nicht richtig und zu dogmatisch.
Oft liegt der Fall vor, dass ein Objekt zwei bis drei Interfaces implementiert.
Je nachdem muss man dann zwangsläufig testen, ob ein bestimmtes Interface unterstützt wird.

Siehe auch die obigen Beispiele.
Hast du nur ein Objekt von Typ IInteger vorliegen, musst aber schauen ob ISomeThing unterstützt wird (oder umgekehrt) -> wie sollte man es sonst machen?

OlafSt 12. Dez 2013 12:21

AW: Umgang mit Interfaces
 
Eine Frage zum Verständnis für jemanden, der mit Interfaces noch nie zu tun hatte:

Delphi-Quellcode:
var
  MySomeThingObject : ISomeThing; // <--- das ist so richtig
begin
  MySomeThingObject := TMyDoSomething.Create;
  if Supports(MySomeThingObject, IInteger) then
  begin
    ShowMessage('IInteger wird voll unterstützt!!!');
  end;
  MySomeThingObject.DoSomeThing;
end;
Sehe ich das richtig, das mein
Delphi-Quellcode:
MySomeThingObject
bei Verlassen der Prozedur automatisch freigegeben wird, obwohl ich zuvor mit
Delphi-Quellcode:
TMyDoSomething.Create;
Speicher dafür alloziert habe ?

mezen 12. Dez 2013 12:27

AW: Umgang mit Interfaces
 
Wenn in TMyDoSomething Referenzzählung enthalten ist (z.B. weil von TInterfacedObjekt abgelitten), dann ja, wird es bei Erreichen von 0 Referenzen automatisch freigegeben.

Der schöne Günther 12. Dez 2013 12:29

AW: Umgang mit Interfaces
 
Und da die einzige Referenz darauf eine lokale Variable war, ist der Referenzzähler beim Verlassen der Methode Null.

Nicht nur beim normalen Verlassen, natürlich auch, wenn deine Methode mittendrin per Exception rausfliegt. Damit spart man sich das ganze nervige
Delphi-Quellcode:
try..finally..Destroy
-Geraffel.

jaenicke 12. Dez 2013 12:43

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1239552)
Nicht nur beim normalen Verlassen, natürlich auch, wenn deine Methode mittendrin per Exception rausfliegt. Damit spart man sich das ganze nervige
Delphi-Quellcode:
try..finally..Destroy
-Geraffel.

Aber nur im Quelltext, bei Interfaces wird das nur mit noch mehr automatisch generiertem Code beim Kompilieren hinzugefügt.

Der schöne Günther 12. Dez 2013 12:50

AW: Umgang mit Interfaces
 
Ich habe in meinem Leben bislang nie den Mut gehabt, mir den Assemblercode anzuschauen, der für die Referenzzählung oder das Werfen einer Exception entsteht.

Dann würde ich jedes mal wenn ich in einer Methode mittels Interface-Variable über irgendetwas iterieren,ein schlechtes Gewissen bekommen und würde meines Lebens nicht mehr froh.


Ja, mir ging es jetzt nur um meinen persönlichen Komfort im Quelltext. Wie sehr der PC dann später zusätzlich schuften muss habe ich jetzt nicht bedacht...

TiGü 12. Dez 2013 13:13

AW: Umgang mit Interfaces
 
Zitat:

Zitat von OlafSt (Beitrag 1239546)
Sehe ich das richtig, das mein
Delphi-Quellcode:
MySomeThingObject
bei Verlassen der Prozedur automatisch freigegeben wird, obwohl ich zuvor mit
Delphi-Quellcode:
TMyDoSomething.Create;
Speicher dafür alloziert habe ?

Wie oben erwähnt, ja!

Mit einem normalen Objekt hättest du ein Speicherleck!

jaenicke 12. Dez 2013 13:36

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1239560)
Ja, mir ging es jetzt nur um meinen persönlichen Komfort im Quelltext. Wie sehr der PC dann später zusätzlich schuften muss habe ich jetzt nicht bedacht...

Wenn du Interfaces benutzt, sind es jeweils rund 15-20 Assemblerbefehle für das Hinzufügen der Referenz und das Entfernen. Es ist also nicht so furchtbar viel, läppert sich aber durchaus, wenn man damit intensiv arbeitet.

Patito 13. Dez 2013 12:45

AW: Umgang mit Interfaces
 
Zitat:

Zitat von TiGü (Beitrag 1239497)
Wer meint es besser zu wissen bzw. sein Anwendungsfall es unbedingt erfordert, muss ja nicht von TInterfacedObject ableiten,

Die RefCount Methoden kann man zwar überschreiben, aber die Grundproblematik von Delphi wird man dadurch nicht los.
Delphi ruft z.B weiterhin lustig die Refenz-Count Methoden von Objekten auf, auch wenn das Objekt z.B. schon freigegeben wurde.
Die zur Zeit einzig sinnvolle Lösung für das Problem nennt sich Free-Pascal...

Zitat:

Zitat von TiGü (Beitrag 1239497)
Die "automatische" Referenzverwaltung von Inferface-Objekten ist in meinen Augen sehr bequem und ermöglicht ein sehr komfortables modernes programmieren.

Ich denke, dass jemand, der "automatische" Referenzverwaltung für bequem hält, vom Thema insgesamt herzlich wenig verstanden hat.
Das ist fast genauso beknackt wie die Forderung der ActiveX Retro-Gurus, dass man Objekte und Interfaces nicht mischen sollte.

Leute. Nehmt einfach einen vernünftigen Compiler und reitet nicht für immer rum auf diesen Lehrsätzen für COM-Interfaces, die Microsoft mal in der Antike verkündet hat.

Zitat:

Zitat von TiGü (Beitrag 1239497)
Hast du nur ein Objekt von Typ IInteger vorliegen, musst aber schauen ob ISomeThing unterstützt wird (oder umgekehrt) -> wie sollte man es sonst machen?

Ein Fehler (z.B. bei dem Problem mit der Liste) liegt schon an der Stelle, wo man so ein Objekt erzeugt und es dann in einen anonymen Container wirft. Eine bessere Lösung wäre die Objekte in typsicheren Listen zu speichern, und nicht erst zur Laufzeit die Objekte herauszufiltern. Wer Typsicherheit von der Compilezeit in die Laufzeit schiebt, darf sich nicht beschweren, dass es Probleme gibt.

Daniel 13. Dez 2013 12:49

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Patito (Beitrag 1239679)
Ich denke, dass jemand, der "automatische" Referenzverwaltung für bequem hält, vom Thema insgesamt herzlich wenig verstanden hat.

Oder ist es anders herum der Fall? Warum sollte ich als ahnungslos gelten, wenn ich z.B. ARC verwende? Unter ObjectiveC habe ich z.B. gar keine andere Wahl. Du verteidigst hier flammend FreePascal - das ist fein für Dich, aber ein klein wenig solltest Du dennoch über den Tellerrand schauen. ;-)

TiGü 13. Dez 2013 13:32

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Patito (Beitrag 1239679)
Zitat:

Zitat von TiGü (Beitrag 1239497)
Wer meint es besser zu wissen bzw. sein Anwendungsfall es unbedingt erfordert, muss ja nicht von TInterfacedObject ableiten,

Die RefCount Methoden kann man zwar überschreiben, aber die Grundproblematik von Delphi wird man dadurch nicht los.

Ehrlich gesagt kann ich dir jetzt nicht mehr so ganz folgen was denn die "Grundproblematik" sein soll.
Kannst du hierauf nochmal eingehen?
Das es COM-Interfaces sind - also einen gewissen sprachübergreifenden Standard folgen - ist jetzt aber nicht das "Problem", oder?

Zitat:

Zitat von Patito (Beitrag 1239679)
Delphi ruft z.B weiterhin lustig die Refenz-Count Methoden von Objekten auf, auch wenn das Objekt z.B. schon freigegeben wurde.
Die zur Zeit einzig sinnvolle Lösung für das Problem nennt sich Free-Pascal...

Dann lass es doch weiterhin lustig aufrufen.
Solange du in den selbstimplementierten _AddRef und _Release Methoden kein Schindluder betreibst geht das doch?!?
Wo ist das Problem?
Oder verhält sich das in Free-Pascal anderes?

Delphi-Quellcode:
  ISomeThing = Interface(IInterface)
  ['{16CCA417-F4C4-4B4A-88CE-FDD79B875876}']
    function DoSomeThing : Integer;
  End;

  TMyInterfacedObject = class(TObject, IInterface)
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

  TMyDoSomething = Class(TMyInterfacedObject, ISomeThing)
  protected
    function DoSomeThing : Integer;
  end;

...

function TMyInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

function TMyInterfacedObject._AddRef: Integer;
begin
  Result := -1;
end;

function TMyInterfacedObject._Release: Integer;
begin
  Result := -1;
end;

...

var
  MySomeThingObject : ISomeThing;
begin
  MySomeThingObject := TMyDoSomething.Create;
  MySomeThingObject.DoSomeThing;
  (MySomeThingObject as TMyDoSomething).Free;

  // folgendes funktioniert problemlos, obwohl das Objekt freigeben wurde! 
  MySomeThingObject._AddRef;
  MySomeThingObject._AddRef;
  MySomeThingObject._AddRef;
  MySomeThingObject._Release;
end;


Zitat:

Zitat von Patito (Beitrag 1239679)
Zitat:

Zitat von TiGü (Beitrag 1239497)
Die "automatische" Referenzverwaltung von Inferface-Objekten ist in meinen Augen sehr bequem und ermöglicht ein sehr komfortables modernes programmieren.

Ich denke, dass jemand, der "automatische" Referenzverwaltung für bequem hält, vom Thema insgesamt herzlich wenig verstanden hat.
Das ist fast genauso beknackt wie die Forderung der ActiveX Retro-Gurus, dass man Objekte und Interfaces nicht mischen sollte.

Leute. Nehmt einfach einen vernünftigen Compiler und reitet nicht für immer rum auf diesen Lehrsätzen für COM-Interfaces, die Microsoft mal in der Antike verkündet hat.

Ja nun, ich denke mir mal meinen Teil dazu.
Es gab immer Leute die meinen es besser zu wissen. :stupid:

COM ist aktueller denn je, weiß nicht was das mit Antike zu tun haben sollte.
Mein Rechner läuft auf Windows 8.1 und da ist das "Kachelstartmenü" durchgehend mit dieser Technologie umgesetzt.

Zeige uns doch bitte mal einen typischen Anwendungsfall in deiner Applikation, wo der Einsatz von COM-Interfaces zu Fehlern führt und stattdessen das von dir gewünschte Verhalten besser wäre.

In meiner Applikation sind ein Großteil der Klassen über Interfaces ansprechbar und das nicht mischen von Objekt- und Interfacereferenzen gewöhnt man sich auch schnell an.
Weiß nicht worin da die Schwierigkeit besteht das konsequent durchzuziehen.
Gleichermaßen könnte man sich darüber aufregen, dass der Compiler nicht folgendes zur Compilezeit anmeckert:

Delphi-Quellcode:
 
var
  a, b, c : Integer;
begin
  a := 0;
  c := b div a;
end;

himitsu 13. Dez 2013 13:39

AW: Umgang mit Interfaces
 
Zitat:

Zitat von Patito (Beitrag 1239679)
Delphi ruft z.B weiterhin lustig die Refenz-Count Methoden von Objekten auf, auch wenn das Objekt z.B. schon freigegeben wurde.

Dann liegt der Fehler aber auch bei dir.

Wenn du "referenzählende" Variablen hast, und du denen unterm Arsch weg die Objekte klaust, wovon die natürlich nichts mitbekommen,
dann können die nur davon ausgehen, daß die darin verlinkte Instanz gültig ist und es werden die Referenzen gezählt (bzw. es wird versucht).

- Entweder es wird über die Referenzzählung der Speicher freigegeben
- und wenn nicht, dann darf die Variable nicht referenzzählend sein
- oder du mußt die referenzzählende Variable auf nil setzen (unter böswilliger Umgehung der Referenzzählung), sobald du das Objekt freigibst, oder bevor die Variable freigegeben wird, bzw. bevor sie einen neuen Wert bekommt

- und es darf auch keiner mehr die "ungültige" Referenz daraus verwenden



Zitat:

Zitat von TiGü (Beitrag 1239683)
Gleichermaßen könnte man sich darüber aufregen, dass der Compiler nicht folgendes zur Compilezeit anmeckert:

Der meckert doch?

B ist nicht initialisiert :stupid:


Zitat:

Zitat von TiGü (Beitrag 1239683)
// folgendes funktioniert problemlos, obwohl das Objekt freigeben wurde!

Die Speicherverwaltung ht den speicher aber "oftmals" noch nicht sofort freigegeben.

Das kann man aber zum Debuggen beheben.
- FastMM entsprechend einstellen
- oder einen Debug-Speichermanager verwenden

welche die Objektinstanzen zerstören/überschreiben, womit des beim nächsten Zugriff knallt


PS: Zugrif auf "ungültige" Zeiger .... wie oben erwähnt.
Das Objekt ist weg, aber DU hast den Instanzzeiger in MySomeThingObject nicht bereinigt.

TiGü 13. Dez 2013 14:23

AW: Umgang mit Interfaces
 
Zitat:

Zitat von himitsu (Beitrag 1239684)
Zitat:

Zitat von TiGü (Beitrag 1239683)
Gleichermaßen könnte man sich darüber aufregen, dass der Compiler nicht folgendes zur Compilezeit anmeckert:

Der meckert doch?

B ist nicht initialisiert :stupid:

:-D

Zitat:

Zitat von himitsu (Beitrag 1239684)
PS: Zugrif auf "ungültige" Zeiger .... wie oben erwähnt.
Das Objekt ist weg, aber DU hast den Instanzzeiger in MySomeThingObject nicht bereinigt.

Eben das wollte ich zeigen!
Solange der Pointer nicht ungültig wird, wird ja auch beliebig in die Methoden gesprungen.
Wenn hier fröhlich Interface- und Objektreferenzen gemischt werden (sollt ihr dafür alle in der Hölle schmoren) dann geht auch folgends:
Delphi-Quellcode:
function TMyDoSomething.DoSomeThing : Integer;
begin
  Result := 123;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  MySomeThingObject : TMyDoSomething;
  MagicNumber : Integer;
begin
//  MySomeThingObject := TMyDoSomething.Create;
  MagicNumber := MySomeThingObject.DoSomeThing;
// MagicNumber hat jetzt den Wert 123, obwohl kein Objekt erzeugt wurde! Schwarze Magie? ;-)
end;
Von daher soll der Compiler doch ruhig in die eigenen _AddRef- und _Release-Methoden springen, wenn man meint sowas brauchen zu müssen!

Whookie 13. Dez 2013 17:18

AW: Umgang mit Interfaces
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich seh schon ... heißes Thema :twisted: ...

Da es mir aber um die Implementierung mit Interfaces geht habe ich das ganze nochmals vereinfacht und komplett auf Interfaces umgestellt. Dazu gibts ein eigenes IAdmin-Interface in dem die Sachen definiert sind die vorher eben nur in der Objektinstanz zur Verfügung standen.

Die Implementierung habe ich hier angehängt weil es noch ein Problem bei der Verwaltung der Interfaces gibt und ja, ohne FastMM gibts keine vernünftige Delphi-Entwicklung :stupid:!

Nochmals zur Erklärung: Die "Hauptliste" (fIntfLst) wird aus einer Textdatei erstellt, es gibt um die 20 verschiedenen Klassen die aus der Kombination von ca. 10 Interfaces bestehen. Diese "Knoten" stehen noch untereinander in Beziehung und daher speichern die Knoten diese Beziehungen in privaten Listen (wie z.B.: TTest.fMyNodes)...

Eigentlich dachte ich ja, das bei reiner Interface-Verwendung keine Probleme mehr auftreten, aber irgendwie schaffe ich es durch meine Querverlinkung die Referenzzählung durcheinander zu bringen (siehe uImpl.pas Zeile 170)...

himitsu 13. Dez 2013 18:29

AW: Umgang mit Interfaces
 
Wenn der Speichermanager aufräumt, dann wird es auch knallen. Oder wenn inzwischen etwas Anderes an der Stelle steht, dann produziert man damit einen Buffer-Overrun,
wenn man auf ungültige Variablen zugreift.

Und das ist nicht erst seit Interfaces so.
Delphi-Quellcode:
var
  o: TMyIrgendwas;

o := TMyIrgendwas.Create;
o.Free;
o.DoWas; // Fehler

o := TMyIrgendwas.Create;
FreeAndNil(o);
o.DoWas; // Zugriffsverletzung bei Addresse 0


Man kann gern die Speicherverwaltung komplett der Referenzzählung des Interfaces überlassen.

Und wenn es unbedingt sein muß, dann holt man sich "kurzzeitig" nochmal eine Objekt-Referenz aus dem Interface raus (an dieser Stelle muß parallel eine Interface-Referenz vorhanden sein), denn Delphi bietet seit einer Weile ein Pseudo-Interface an, welches die Objektreferenz zurückgibt. (wenn im Interface ein Delphi-Objekt steckt)
Und an diese Objektreferenz kommt man z.B. via
Delphi-Quellcode:
intf AS TObject
ran.
Man kann natürlich auch eigene Getter in seinem Interface verbauen, welches Objekt-Instanzen zurückgibt.
Sobald dann die "paralelle" Interface-Referenz freigegeben wird, ist die Objektreferenz als "ungültig" anzusehen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:53 Uhr.
Seite 1 von 2  1 2      

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