【问题标题】:Accessing Sub functions /procedures from DPR or other function / procedure in Delphi从 DPR 或 Delphi 中的其他函数/过程访问子函数/过程
【发布时间】:2010-05-19 14:26:57
【问题描述】:

据我所知 - 子程序对其父函数/过程具有私有访问模式,对吗?

有没有办法从“外部世界”访问它们 - dpr 或单元中的其他功能/程序?

另外 - 哪种方式需要更多的计算和空间来编译文件?

例如:

function blablabla(parameter : tparameter) : abcde;
 procedure xyz(par_ : tpar_);
 begin
  // ...
 end;
begin
 // ...
end;

procedure albalbalb(param : tparam) : www;
begin
 xyz(par_ : tpar_); // is there any way to make this function public / published to access it therefore enabling to call it this way?
end;

// all text is random.

// also, is there way to call it from DPR in this manner?

// in C++ this can be done by specifing access mode and/or using "Friend" class .. but in DELPHI?

【问题讨论】:

  • Private 是 private :) 您是否正在寻找一种在不更改代码的情况下将其公开的方法?
  • 它甚至不是私有的,它是内部的。它不存在于嵌入它的“父”例程之外。

标签: delphi access-specifier subroutine


【解决方案1】:

嵌套过程/函数 - 在另一个过程或函数中声明的那些是一种特殊类型,因为它们可以访问它们嵌套的过程的堆栈(以及参数/局部变量)。因此,和 Delphi 范围规则,没有办法在“父”程序之外访问它们。只有在需要利用它们的特殊功能时才使用它们。 AFAIK Delphi/Pascal 是少数具有此功能的语言之一。从编译器的角度来看,该调用有一些额外的代码来允许访问父堆栈帧 IIRC。 C ++中的AFAIK“朋友”类/函数是不同的-它们是类访问方法,而在您的示例中,您使用的是普通过程/函数。 在 Delphi 中,在同一单元中声明的所有过程/类都自动成为“朋友”,除非在最新的 Delphi 版本中使用了 strict private 声明。例如,只要所有内容都在同一个单元中,此代码 sn-ps 就可以工作:

  type
    TExample = class
    private
      procedure HelloWorld;
    public
    ...
    end;

  implementation

    function DoSomething(AExample: TExample);
    begin
      // Calling a private method here works
      AExample.HelloWordl;
    end;

【讨论】:

    【解决方案2】:

    注意:嵌入式例程 私有/受保护方法。

    嵌入式例程,即例程内部的例程不能被外部例程访问。 您发布了一个嵌入式例程的示例,我还听说它们称为内部例程。

    这是另一个例子:

    procedure DoThis;
    
    function DoThat : Boolean;
    begin
      // This Routine is embedded or internal routine.
    end;
    begin
    
    // DoThat() can only be accessed from here no other place.
    
    end;
    

    无论可见性如何,类上的方法都可以使用 Delphi 2010 通过 RTTI 调用。我在this article中详细说明了如何做到这一点。

    如果你在同一个 Unit 类上的方法可以被任何其他代码访问而不管可见性,除非它们被标记为 Strict Private。 This Questionaccepted answer 中有更多详细信息和很好的示例代码。

    如果你在两个不同的单位,你可以使用受保护的方法破解来访问受保护的方法。详情见this article

    【讨论】:

      【解决方案3】:

      是的,您可以从外部世界访问嵌套在其他(父)子程序中的子程序。虽然有点棘手。我在网上找到了这个方法。

      如何将嵌套例程作为程序参数(32 位)传递

      Delphi 通常不支持将嵌套例程作为过程参数传递:

      // This code does not compile:
      procedure testpass(p: tprocedure);
      begin
        p;
      end;
      procedure calltestpass;
       procedure inner;
       begin
         showmessage('hello');
       end;
      begin
        testpass(inner);
      end;
      

      显而易见的解决方法是在 testpass 中传递过程地址并对其进行类型转换:

      // This code compiles and runs OK
      procedure testpass(p: pointer);
      begin
        tProcedure(p);
      end;
      procedure calltestpass;
       procedure inner;
       begin
         showmessage('hello');
       end;
      begin
        testpass(@inner);
      end;
      

      然而,在上面的例子中有一个陷阱——如果“内部”例程引用了在从 testpass 调用“内部”过程之前被压入堆栈的任何变量(calltestpass 参数 - 如果有的话,或者calltestpass 中的局部变量 - 如果有的话),你的系统很可能会崩溃:

      // This code compiles OK but generates runtime exception (could even be
      //  EMachineHangs :-) )
      procedure testpass(p: pointer);
      begin
        tProcedure(p);
      end;
      procedure calltestpass;
      var msg: string;
       procedure inner;
       begin
         msg := 'hello';
         showmessage(msg);
       end;
      begin
        testpass(@inner);
      end;
      

      原因,简单来说就是栈帧排列 被调用 testpass 例程和“内部”过程“破坏” 错误地计算参数和局部变量位置 (请不要责怪德尔福)。 解决方法是在之前设置正确的堆栈上下文 从“testpass”中调用“inner”。

      // This code compiles and runs OK
      {$O-}
      procedure testpass(p: pointer);
      var callersBP: longint;
      begin
        asm // get caller's base pointer value at the very beginning
          push dword ptr [ebp]
          pop callersBP
        end;
      // here we can have some other OP code
        asm // pushes caller's base pointer value onto stack and calls tProcedure(p)
          push CallersBP
          Call p
          Pop  CallersBP
        end;
      // here we can have some other OP code
      end;
      {$O+}
      
      procedure calltestpass;
      var msg: string;
       procedure inner;
       begin
         msg := 'hello';
         showmessage(msg);
       end;
      begin
        testpass(@inner);
      end;
      

      请注意,对于 testpass 例程,优化已关闭 - 优化通常不能很好地处理混合的 OP/汇编代码。

      【讨论】:

        【解决方案4】:

        不,没有办法按照您的要求进行操作。 xyz 函数只能被封闭的blablabla 函数调用。在该函数之外,xyz 不在范围内,无法命名。如果 C++ 允许嵌套函数,那么也没有任何方法可以引用它,就像无法从当前翻译单元外部引用具有静态链接的函数一样。

        如果您需要从blablabla 函数外部调用xyz,则将xyz 移到外部。如果您需要从当前单元外部调用它,则需要在单元的接口部分中声明该函数。然后,将该单元添加到外部代码的uses 子句中,您就可以从任何您想要的地方调用xyz,甚至是DPR 文件。

        如果xyz 引用blablabla 函数的变量或参数,那么您需要将它们作为参数传递,否则xyz 将无法再访问它们。

        访问说明符的概念在这里并不重要,因为我们不是在谈论类。单元具有 interfaceimplementation 部分,它们与类的 publicprivate 部分实际上不同.

        【讨论】:

          猜你喜欢
          • 2010-10-13
          • 1970-01-01
          • 1970-01-01
          • 2010-10-08
          • 2014-08-30
          • 1970-01-01
          • 2010-11-11
          • 1970-01-01
          • 2019-12-18
          相关资源
          最近更新 更多