【问题标题】:How do I call protected methods using class helper?如何使用类助手调用受​​保护的方法?
【发布时间】:2014-09-18 16:50:02
【问题描述】:

假设我们有一个类,其方法可能非常有用,但由于受保护范围而无法使用:

unit Sealed;

interface

type
  TGeneral = class(TObject)
    { this method is useful, but not available }
    protected procedure Useful; virtual;
  end;

  TSpecific1 = class(TGeneral)
    { some descendants override `Useful` method }
    protected procedure Useful; override;
  end;

  TSpecific2 = class(TGeneral)
    { and some dont, but inherit `Useful`ness from the parent }
  end;

我知道两种使用这种方法的老式方法,都涉及继承和类型转换。这两种方法都应该适用于基本案例 #1 和高级多态案例 #2。

program CallingSite;

uses Sealed;

function GetInstance: TGeneral;
begin
  { !PSEUDO! makes compiler happy about the rest of code }
  // depending on use case supposed to return an instance of `TGeneral`
  // or any of its descendants - `TSpecific1`, `TSpecific2`
end;

type
  { this makes a current module a "friend" for `TGeneral` }
  TFriend = class(TGeneral)
  end;

procedure Case1;
var
  { holds an instance of `TGeneral` }
  General: TGeneral;
begin
  General := GetInstance;
  { protected method is available for "friend" via static cast }
  TFriend(General).Useful;  // compiles!
end;

type
  TIntroducer = class(TGeneral)
  { this "reintroduces" `Useful` method to public scope }
  public procedure Useful; override;
  // this approach ought to work even with strict protected methods
  // !!! but I THINK it is UNSAFE to use on virtual and/or dynamic methods
  end;

procedure TIntroducer.Useful;
begin
  { and calls `Useful` via wrapper }
  inherited;
end;

procedure Case2;
var
  { polymorphic instance of any `TGeneral`'s descendant }
  Specific: TGeneral;
begin
  Specific := GetInstance;
  { protected method is callable via public wrapper, static cast again }
  TIntroducer(Specific).Useful; // compiles!
end;

我想知道:

  • 如何利用类助手的强大功能实现相同的结果?
  • 是否也可以使用类助手调用私有方法?
  • 案例 #1 和案例 #2 之间会有什么区别,因为哪个类帮助器扩大了类范围,而不是内部表示?
  • 如何从类助手中重新引入的方法中调用原始方法而不会冒递归风险?

另外,请评论关于TIntroducer不安全的评论。

【问题讨论】:

    标签: delphi oop class-helpers


    【解决方案1】:

    你可以这样使用助手:

    unit Unit2;
    
    interface
    
    type
      TGeneral = class(TObject)
        protected procedure Useful; virtual;
      end;
    
      TSpecific2 = class(TGeneral)
      end;
    
      TSpecificHelper = class helper for TGeneral
      public
        procedure ExposedUseful;
      end;
    
    implementation
    
    procedure TGeneral.Useful;
    begin
      WriteLn('general');
    end;
    
    procedure TSpecificHelper.ExposedUseful;
    begin
      Useful;
    end;    
    
    end.
    

    这些甚至可以在单独的单元中声明,例如:

    unit Unit2;
    
    interface
    
    type
      TGeneral = class(TObject)
        protected procedure Useful; virtual;
      end;
    
    implementation
    
    procedure TGeneral.Useful;
    begin
      WriteLn('general');
    end;
    
    end.
    

    分开

    unit Unit3;
    
    interface
    uses
      Unit2;
    type
      TSpecific2 = class(TGeneral)
      end;
    
      TSpecificHelper = class helper for TGeneral
      public
        procedure ExposedUseful;
      end;
    
    implementation
    
    procedure TSpecificHelper.ExposedUseful;
    begin
      Useful;
    end;
    
    end.
    

    并进行测试:

    program Project1;
    
    
    {$APPTYPE CONSOLE}
    
    uses
      //Unit2,  // either or
      Unit3;
    
    var
      foo : TSpecific2;
    begin
      foo := TSpecific2.Create;
      foo.ExposedUseful;
      Readln;
    end.
    

    如果您为基类创建助手,则可以以类似的方式公开私有成员。但是,如果在不同的单元中,则需要演员表。例如:

    // in Unit2
    TGeneral = class(TObject)
        private
          procedure AlsoUseful;
        protected
          procedure Useful; virtual;
      end;
    
    //in Unit3
    
      TSpecificHelper = class helper for TGeneral
      public
        procedure ExposedUseful;
        procedure ExposedAlsoUseful;
      end;
    
    // ...
    implementation
    
    procedure TSpecificHelper.ExposedAlsoUseful;
    begin
      TGeneral(self).AlsoUseful;
    end;
    

    至于多态性,你真的可以自己测试一下。帮助器将应用于您的实例派生自的任何后代类:

      TSpecific1 = class(TGeneral)
        protected
          procedure Useful; override;
      end;
    
    // ...
    
    procedure TSpecific1.Useful;
    begin
      WriteLn('specific 1');
    end;
    

    在哪里

    TSpecific2 = class(TSpecific1)
    end;
    

    当使用上述基类的助手调用时,将产生输出specific 1

    注意
    从 Delphi 10.1 Berlin 开始,类助手不能再访问严格保护、严格私有或私有成员。这个“功能”实际上是 Embarcadero 现在在柏林修复的编译器错误。
    使用助手访问普通的受保护成员仍然是可能的。

    【讨论】:

    • 谢谢!那么,在避免递归的同时,不可能保留原来的方法名吗?
    • 如果不深入了解TSpecificN 实现的可怕细节,多态性会起作用吗? eg: helper 能调用虚方法吗?
    • @FreeConsulting 不,如果您尝试使用相同的名称,堆栈溢出应该非常明显。编辑回答您的第二个问题。
    • @FreeConsulting 说了这么多,建议您也阅读一下:deltics.co.nz/blog/posts/273
    猜你喜欢
    • 2013-09-27
    • 2011-10-09
    • 2011-05-18
    • 2013-02-04
    • 2015-12-08
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    相关资源
    最近更新 更多