是否可以使用在接口部分的实现部分中声明的 class

Is it possible to use a class declared in Implementation section from Interface section

如果我没理解错,interface段对其他单位可见,implementation段只在当前.pas文件可见

我有两个class,classTA应该是外面可见的,其他的classTB不应该,但是我需要一个TA.

TB 类型的字段
interface
    
type
  TA = class
    //something
    B : TB;
  end;
    
//something
    
implementation
    
type
  TB = class
    //something
  end;

不是这样的。我也不能使用前向声明。有办法吗?

或者,有没有办法在 interface 部分中声明 TB 但使其有点私有?

Is it possible to use a class declared in Implementation section from Interface section?

没有

Or is there a way to declare TB in the Interface section but make it kinda private?

是的,如果您将其设为嵌套 class,请在包含类型的私有部分中声明。

类型在声明之前不能使用(根据行号)。特别是,这意味着您不能在 interface 部分中使用在 implementation 部分中声明的类型。

但是,请考虑以下示例:

unit VisibilityTest;

interface

type
  TFrog = class
  strict private type
    TFrogMetabolism = class
      procedure DoAnabolismStuff;
      procedure DoCatabolismStuff;
    end;
  strict private
    FMetabolism: TFrogMetabolism;
  public
    procedure Croak;
    procedure Walk;
    procedure Jump;
  end;

implementation

{ TFrog.TFrogMetabolism }

procedure TFrog.TFrogMetabolism.DoAnabolismStuff;
begin

end;

procedure TFrog.TFrogMetabolism.DoCatabolismStuff;
begin

end;

{ TFrog }

procedure TFrog.Jump;
begin

end;

procedure TFrog.Croak;
begin

end;

procedure TFrog.Walk;
begin

end;

end.

此处 TFrog class 对其他单位可见,以及它的 CroakWalkJump 方法。

它确实有一个(strict private 在本例中)类型为 TFrogMetabolism 的字段,该类型只能在 TFrog 内部使用——因此只能在该单元内部使用-- 由于前面的 strict private 规范。

这应该会给您一些想法。可能有几种变体:

  • 如果从 strict private type 中删除 strict,则 TFrogMetabolism class 可以在这个特定单元内的任何地方使用,而不仅仅是 TFrog.

  • 如果您将 private 替换为 protected,则 class 也可以用于 class 而非 TFrog ] 但派生自 TFrog.

一个选项可能是声明 TB 实现的 public interface,然后 TA 可以拥有该接口类型的字段。

interface
    
type
  IB = interface
    //something
  end;

  TA = class
  public
    B : IB;
    constructor Create;
  end;
    
//something
    
implementation
    
type
  TB = class(TInterfacedObject, IB)
    //something
  end;

constructor TA.Create;
begin
  B := TB.Create;
end;

你可以做到,但要付出代价。在classTA中,引用TB的变量必须是TObject类型。让我们将该变量命名为 B。您可以将 class TB 的实例分配给该变量,例如从构造函数中。然后当TA中的代码需要使用变量B时,它必须转换为TB(硬转换或使用“As”运算符)。

您还应该在该 TB 上禁用 RTTI,以便外部无法发现 TB 中的内容。

代码如下:

unit Unit24;

interface

uses
    System.SysUtils;

type
    TA = class
        B : TObject;  // Will contain a TB instance
        constructor Create;
        destructor Destroy; override;
        procedure Demo;
    end;

implementation

type
    TB = class
        procedure SayHello;
    end;

{ TA }

constructor TA.Create;
begin
    inherited Create;
    B := TB.Create;
end;

procedure TA.Demo;
begin
    TB(B).SayHello;
end;

destructor TA.Destroy;
begin
    FreeAndNil(B);
    inherited Destroy;
end;

{ TB }

procedure TB.SayHello;
begin
    WriteLn('Hello!');
end;

end.

使用示例:

program Project24;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  Unit24 in 'Unit24.pas';

var
  A : TA;
begin
  A := TA.Create;
  try
      A.Demo;
  finally
      A.Free;
  end;
end.

指针和class可以向前声明,但必须在同一类型块中声明。这样做的原因是因为它们是引用类型,甚至只是前向声明就固定了它们之后字段的偏移量。类型块位主要是为了方便编译器编写者(但通常也会 easier/better 生成错误消息)

Pascal 后续 Modula2 允许在实现中更严格地定义指针,例如指向某个记录的指针(所谓的不透明类型)。其他单元只能将它用作句柄类型(传递它等),并且实现可以在没有类型转换的情况下访问细节。通过这种方式,它是一种语言辅助方式来执行 Fpiette 建议的操作,对象是 class.

的最基本子集

另一种解决方案是使其成为通用的,并专门用于通用 TB 类型的实现。