【问题标题】:Is it possible to use a class declared in Implementation section from Interface section是否可以使用接口部分的实现部分中声明的类
【发布时间】:2021-06-21 16:44:01
【问题描述】:

如果我理解正确,interface 部分对其他单位可见,implementation 部分仅在当前的.pas 文件中可见。

我有两个类,类 TA 应该对外部可见,其他类 TB 不应该,但我需要 TA 中的 TB 类型的字段。

interface
    
type
  TA = class
    //something
    B : TB;
  end;
    
//something
    
implementation
    
type
  TB = class
    //something
  end;

它不是那样工作的。我也不能使用前向声明。有什么办法吗?

或者,有没有办法在interface 部分声明TB,但将其设为私有?

【问题讨论】:

  • 为什么TA 需要一个引用尚未定义的类型的字段?这里的目标是什么?
  • 你有这个用例吗?
  • 如果您在界面部分提到 TB,那么您将其向公众开放。必须在界面中知道。使用@RemyLebeau 接口解决方案。在这种情况下,您只是打开接口,而不是它的实现。

标签: class oop delphi visibility pascal


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    你可以做到,但要付出代价。在 TA 类中,引用 TB 的变量必须是 TObject 类型。让我们将该变量命名为 B。您可以将 TB 类的实例分配给该变量,例如从构造函数中。那么当 TA 中的代码需要使用变量 B 时,必须将 is 强制转换为 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.
    

    【讨论】:

    • 是的,我必须承认我偶尔会做这样的事情。但我总是尽量避免它,因为你不想不必要地放弃类型安全。
    • @AndreasRejbrand 你并没有完全放弃提供的类型安全——正如我在帖子中所建议的——你使用“as”运算符。 WinApi 充满了类似的结构。许多“句柄”实际上是指向未公开结构的指针。
    • 对不起,我的意思是“编译时类型检查”。当然as 比不安全的TSomething(X) 演员更安全。
    【解决方案3】:

    一种选择可能是声明TB 实现的公共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;
    

    【讨论】:

    • 我 认为 OP 甚至希望 IB 界面对其他单位隐藏,但我同意目前尚不清楚 OP 到底想做什么。
    【解决方案4】:

    类型在声明之前不能使用(就行号而言)。特别是,这意味着您不能在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 类对其他单元可见,以及它的Croak、Walk 和Jump 方法。

    并且它确实有一个类型为TFrogMetabolism 的(在此示例中为strict private)字段,该类型只能在TFrog 内部使用——因此只能在这个单元内部使用——因为前面的@987654331 @ 规范。

    这应该会给你一些想法。一些变体是可能的:

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

    • 如果将private 替换为protected,则该类也可以用于不是TFrog 但派生自TFrog 的类。

    【讨论】:

    • 当然,在您的特定情况下,使用嵌套类型(语义上)可能有意义,也可能没有意义。
    【解决方案5】:

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

    没有。

    或者有没有办法在接口部分声明 TB,但让它有点私有?

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-24
      • 2015-01-05
      • 1970-01-01
      • 2011-02-06
      相关资源
      最近更新 更多