Es gibt im Zusammenhang mit Interfaces eine wichtige Grundregel:
Arbeite niemals mit dem Objekt und den Interfacen gleichzeitig!
(Wenn man
genau weiß was man tut, kann man das trotzdem machen, aber es ist nicht empfehlenswert)
Ich hab dir im folgenden dein Beispiel mal umgebaut. Um es nicht zu kompliziert zu machen, wird der Wert jetzt im Konstruktor übergeben und ISquare kann seine Zahl als String zurückgeben.
Delphi-Quellcode:
type
ISquare = interface(IInterface)
['{4F30FDE5-9C8C-4F34-A828-FA32B1C0DA3F}']
procedure Square;
function NumberAsString : String;
end;
TInt = class(TInterfacedObject, ISquare)
private
FInt: Integer;
public
constructor Create(ANumber : Integer);
procedure Square;
property Number: Integer read FInt write FInt;
function NumberAsString : String;
end;
TFloat = class(TInterfacedObject, ISquare)
private
FFloat: Single;
public
constructor Create(ANumber : Single);
procedure Square;
property Number: Single read FFloat write FFloat;
function NumberAsString : String;
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
constructor TInt.Create(ANumber: Integer);
begin
FInt := ANumber;
end;
function TInt.NumberAsString: String;
begin
Result := IntToStr(FInt);
end;
procedure TInt.Square;
begin
FInt:= FInt * FInt;
end;
constructor TFloat.Create(ANumber: Single);
begin
FFloat := ANumber;
end;
function TFloat.NumberAsString: String;
begin
Result := FloatToStr(FFloat)
end;
procedure TFloat.Square;
begin
FFloat:= FFloat * FFloat;
end;
procedure SquareIt(aNumber: ISquare); //Fehler ohne const, mit kein Fehler
begin
aNumber.Square;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
aInt,
aFloat: ISquare;
//MyInterface: ISquare; unnötig
begin
aInt:= TInt.Create(5);
//MyInterface:= aInt; //Interface Referenz Counter geht hoch
aFloat:= TFloat.Create(5);
//aInt.Number:= 5; gibts nicht mehr
//MyInterface.Square;
//MyInterface:= nil; //Zähler geht wieder runter
//aFloat.Number:= 5; gibts nicht mehr
aInt.Square;
aFloat.Square;
ShowMessage(aInt.NumberAsString);
ShowMessage(aFloat.NumberAsString);
//SquareIt(aInt); //während der Aufrufe geht der Zähler auch hoch
//SquareIt(Float);
//ShowMessage(IntToStr(aInt.Number));
//ShowMessage(FloatToStr(aFloat.Number));
//aInt.Free;
//aFloat.Free;
end;
Das Problem in deiner Implementation war, das das Objekt freigegeben wurde, nachdem es einmal als Interface benutzt wurde. Das kommt daher das es nach dem erstellen(.Create) den RefCount 0 hat. Durch die Zuweisung an ein Interface oder die Übergabe in eine Funktion als Interface wird der RefCount auf 1 gesetzt. Am Ende der Prozedur oder durch eine Neubelegung der interfacevariable sinkt der RefCount auf 0 und für das Objekt wird .Free aufgerufen. Jede nachfolgende Verwendung wirft dann eine
AV.