这可能是my previous question的扩展名。
我已经理解基于接口的变量不能被定义为其原始类型,否则引用计数不能正常用于自动释放。
但是如果一个类实现了两个接口,那么在创建它的实例时应该定义什么类型?
请考虑以下代码:
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.
那么您首选哪种方式?还是其他任何建议?提前谢谢。
答案 0 :(得分:9)
这里存在很多混乱和复杂性。我会告诉你我将如何做,而不是试图剖析你所拥有的东西。
首先删除TTestObj
类型的所有变量。您应该只使用接口引用。你会想要每个变量。
var
TestIntf: ITestInterface;
AnotherIntf: IAnotherInterface;
请注意,我更改了这些变量的名称,将Obj
后缀替换为Intf
。这反映出它们是接口引用而不是对象引用。
然后你可以这样做:
TestIntf := TTestObj.Create;
AnotherIntf := TestIntf as IAnotherInterface;
现在您有两个接口变量,每个接口都有一个变量。碰巧这两个引用背后的实现对象是同一个对象,这可能是你想要的。
你同样可以颠倒逻辑:
AnotherIntf := TTestObj.Create;
TestIntf := AnotherIntf as ITestInterface;
这可以实现完全相同的效果,无论哪种方式都可以。
如果你想在变量后面有一个不同的实例,那么这很容易:
TestIntf := TTestObj.Create;
AnotherIntf := TTestObj.Create;
这里的关键点是:
as
运算符获取其他接口。