从Delphi中的DPR或其他函数/过程访问子函数/过程

3
据我所知,子例程具有私有访问模式,只能由其父函数/过程访问,对吗?
是否有办法从“外部世界”-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?

私有就是私有的 :) 你是否正在寻找一种方法,使其公开而不更改代码? - mjn
它甚至不是私有的,它是内部的。它不存在于嵌入它的“父”例程之外。 - Francesca
4个回答

7
嵌套过程/函数是指在另一个过程或函数内部声明的一种特殊类型,因为它们可以访问该过程或函数的堆栈(以及参数/局部变量)。因此,在 Delphi 作用域规则下,无法从外部访问它们。只有当您需要利用它们的特殊功能时才使用它们。据我所知,Delphi/Pascal 是少数具备这种特性的语言之一。从编译器的角度来看,调用需要额外的代码来允许访问父堆栈帧。 据我所知,C++ 中的“友元”类/函数不同 - 它们是类访问方法,而在您的示例中,您正在使用普通的过程/函数。 在 Delphi 中,除非在最新版本的 Delphi 中使用了严格私有声明,否则在同一单元中声明的所有过程/类都自动成为“友元”。例如,只要一切都在同一单元中,以下代码片段将起作用:
  type
    TExample = class
    private
      procedure HelloWorld;
    public
    ...
    end;

  implementation

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

5
注意:嵌套例程<>私有/受保护方法。
嵌套例程即嵌套在其他例程内的例程,无法被外部例程访问。 您已经发布了一个嵌套例程的示例,我也听说过它们被称为内部例程。
以下是另一个示例:
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;

无论可见性如何,可以通过RTTI在Delphi 2010中调用类的方法。我在这篇文章中详细介绍了如何做到这一点。
如果您在同一个单元中,无论可见性如何,都可以通过任何其他代码访问类上的方法,除非它们标记为Strict Private。 此问题有更多详细信息和良好示例代码在接受的答案中。
如果您在两个不同的单元中,则可以使用Protected Method Hack来访问受保护的方法。 在这篇文章中详细介绍了这一点。

4

是的,您可以从外部访问嵌套在其他(父级)子程序中的子程序。虽然这有些棘手。我在网络上找到了这个如何做的教程。

如何将嵌套例程作为过程参数传递(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;

然而,上述示例存在一个问题 - 如果“inner”例程引用了在从testpass调用“inner”过程之前推送到堆栈上的任何变量(如果有参数,则为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例程的调用“破坏”了堆栈帧布局,导致“内部”过程错误地计算参数和本地变量位置(请不要责怪Delphi)。 解决方法是在从“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;

请注意,在测试程序中,优化已关闭 - 通常情况下,优化不能很好地处理混合的OP /汇编代码。

2
不,你所要求的做法是不可能实现的。只有封闭的“blablabla”函数才能调用“xyz”函数。在该函数外部,“xyz”不在作用域内,也无法命名它。如果C++允许嵌套函数,也没有办法引用它,就像无法从当前翻译单元的外部引用具有静态链接的函数一样。
如果需要从“blablabla”函数外部调用“xyz”,则需要将“xyz”移出来。如果需要从当前单元的外部调用它,则需要在单元的接口部分中声明该函数。然后,将该单元添加到外部代码的“uses”子句中,就可以从任何地方调用“xyz”,甚至是DPR文件。
如果“xyz”涉及到“blablabla”函数的变量或参数,则需要将它们作为参数传递,因为否则“xyz”将无法访问它们。
这里并不涉及访问修饰符的概念,因为我们没有谈论类。单元有“接口”和“实现”部分,这与类的“公共”和“私有”部分并不完全相同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接