gpt4 book ai didi

delphi - 使用实现两个接口(interface)的对象的最佳实践是什么?

转载 作者:行者123 更新时间:2023-12-03 15:42:54 25 4
gpt4 key购买 nike

这可能是 my previous question 的扩展.

据我了解,基于接口(interface)的变量不能定义为其原始类型,否则引用计数无法正常自动释放。

但是如果一个类实现了两个接口(interface),那么在创建它的实例时应该定义什么类型?

考虑以下代码:

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.

那么您更喜欢哪种方式?或者还有其他建议吗?提前致谢。

最佳答案

这里有很多困惑和复杂的地方。我不会试图剖析你所拥有的东西,而是向你展示我将如何做到这一点。

首先删除所有 TTestObj 类型的变量。您应该仅使用接口(interface)引用。您需要为每个变量分配一个变量。

var
TestIntf: ITestInterface;
AnotherIntf: IAnotherInterface;

请注意,我更改了这些变量的名称,将 Obj 后缀替换为 Intf。这反射(reflect)出它们是接口(interface)引用而不是对象引用。

然后你可以简单地这样做:

TestIntf := TTestObj.Create;
AnotherIntf := TestIntf as IAnotherInterface;

现在您有两个接口(interface)变量,每个接口(interface)对应一个变量。碰巧这两个引用背后的实现对象是同一个对象,这可能就是您想要的。

您同样可以颠倒逻辑:

AnotherIntf := TTestObj.Create;
TestIntf := AnotherIntf as ITestInterface;

这达到了完全相同的效果,您可以采用任何一种方法。

如果您想要变量后面有一个不同的实例,那么这很简单:

TestIntf := TTestObj.Create;
AnotherIntf := TTestObj.Create;

这里的要点是:

  1. 不要混合接口(interface)和对象。一旦开始使用接口(interface),就不要访问其背后的实现对象。
  2. 当一个对象实现了多个接口(interface)时,可以使用as运算符来获取其他接口(interface)。

关于delphi - 使用实现两个接口(interface)的对象的最佳实践是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49008129/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com