This may be an extension of my previous question.
I have understood that an interface-based variable can not be defined as its original type, otherwise the reference count does not work properly for automatic release.
But if a class implements two interfaces, then what type should be defined when make an instance of it?
Consider following code:
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
SysUtils, Classes;
type
ITestInterface = interface(IInvokable)
['{A7BDD122-7DC6-4F23-93A2-B686571AB2C8}']
procedure TestMethod;
end;
IAnotherInterface = interface(IInvokable)
['{15FEC4A7-E361-41D0-9D52-170AFAD1794B}']
procedure AnotherMethod;
end;
TTestObj = class(TInterfacedObject, ITestInterface, IAnotherInterface)
constructor Create;
destructor Destroy; override;
private
FData: TStrings;
public
procedure TestMethod;
procedure AnotherMethod;
end;
{ TTestObj }
constructor TTestObj.Create;
begin
FData := TStringList.Create;
end;
destructor TTestObj.Destroy;
begin
Writeln('Destroy');
FData.Free;
inherited;
end;
procedure TTestObj.TestMethod;
begin
FData.Text := 'TestMethod';
Writeln(FData.Strings[0]);
end;
procedure TTestObj.AnotherMethod;
begin
FData.Text := 'AnotherMethod';
Writeln(FData.Strings[0]);
end;
{ Main }
function CreateObj: TTestObj;
begin
Result := TTestObj.Create;
end;
function CreateObj_i1: ITestInterface;
begin
Result := TTestObj.Create;
end;
function CreateObj_i2: IAnotherInterface;
begin
Result := TTestObj.Create;
end;
procedure Main;
var
TestObj: ITestInterface; // It must be declared as an interface type, or it won't be freed correctly.
AnotherObj: IAnotherInterface;
NaturalObj: TTestObj;
begin
{ 1st way: The syntax is a bit natural, but easily lead to memory leaks. }
CreateObj; // memory leak !
TestObj := CreateObj;
TestObj.TestMethod;
AnotherObj := CreateObj;
AnotherObj.AnotherMethod;
TestObj := nil;
AnotherObj := nil;
Writeln('----------');
{ 2nd way: The syntax is a bit messy, you should do type conversion carefully. }
CreateObj_i1; // object freed correctly.
TestObj := TTestObj(CreateObj_i2); // Using ITestInterface(CreateObj_i2) is wrong.
TestObj.TestMethod;
AnotherObj := TTestObj(CreateObj_i1); // Using IAnotherInterface(CreateObj_i1) is wrong.
AnotherObj.AnotherMethod;
TestObj := nil; // useless, it won't be be freed until the procedure returns.
AnotherObj := nil; // as above.
Writeln('----------');
{ 3rd way: The syntax is a bit natural, but it's easily lead to access violation if pass the `NaturalObj` out of the procedure. }
NaturalObj := TTestObj(CreateObj_i1); // Using TTestObj(CreateObj_i2) is okay too.
NaturalObj.TestMethod;
NaturalObj.AnotherMethod;
end;
begin
Writeln('Program start!');
Main;
Writeln('Program end.');
Readln;
end.
So which way is your preferred? Or any other advice? Thanks in advance.
There is a lot of confusion and complexity here. Rather than trying to dissect what you have, I'll show you how I would do it.
First of all remove all variables of type TTestObj. You should be using interface references only. You'll want a variable for each one.
var
TestIntf: ITestInterface;
AnotherIntf: IAnotherInterface;
Note that I have changed the name of these variables, replacing the Obj suffix with Intf. This reflects that they are interface references rather than object references.
Then you can simply do this:
TestIntf := TTestObj.Create;
AnotherIntf := TestIntf as IAnotherInterface;
Now you have two interface variables, one for each of your interfaces. It so happens that the implementing object behind both of these references is the same object, which is presumably what you want.
You could equally have reversed the logic:
AnotherIntf := TTestObj.Create;
TestIntf := AnotherIntf as ITestInterface;
This achieves exactly the same effect, you can do it either way.
If you want a different instance behind the variables then that is easy enough:
TestIntf := TTestObj.Create;
AnotherIntf := TTestObj.Create;
The key points here are:
as operator to obtain the other interfaces. If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With