function ReturnTwoStrings (out Str1 : String) : String; begin ExecuteProcedure (procedure begin Str1 := 'First String'; Result := 'Second String'; end); end;
当然是非常人为的例子,但我遇到了一些有用的情况.
当我尝试编译它时,编译器抱怨他“无法捕获符号”.此外,当我尝试这样做时,我收到了一个内部错误.
编辑我刚刚意识到它适用于普通参数,如
... (List : TList)
这不像其他案件那样有问题吗?谁保证只要执行匿名方法,引用仍指向活动对象?
解决方法
由于Jon提到的原因,无法验证安全性.由匿名方法创建的闭包可以比创建它的方法激活更长,并且可以类似地比调用创建它的方法的方法的激活更长.因此,捕获的任何var或out参数或Result变量都可能最终成为孤立状态,并且将来从闭包内部对它们的任何写入都会破坏堆栈.
当然,Delphi不在托管环境中运行,并且它没有与例如安全限制相同的安全限制. C#.语言可以让你做你想做的事.但是,在出错的情况下,很难诊断出错误.不良行为将表现为常规变化值中的局部变量,没有可见的近因;如果方法引用是从另一个线程调用的话会更糟.
调试起来相当困难.即使硬件内存断点也是一个相对较差的工具,因为堆栈经常被修改.人们需要在遇到另一个断点时有条件地打开硬件存储器断点(例如,在方法输入时). Delphi调试器可以做到这一点,但我猜想大多数人都不知道这种技术.
更新:关于您的问题的添加,按值传递实例引用的语义在包含闭包的方法之间没有什么不同(并且捕获参数0和不包含闭包的方法.这两种方法都可以保留对通过值传递的参数;不捕获参数的方法可以简单地将引用添加到列表中,或者将其存储在私有字段中.
由参考传递的参数的情况不同,因为呼叫者的期望是不同的.一个程序员这样做:
procedure GetSomeString(out s: string); // ... GetSomeString(s);
如果GetSomeString保持对传入的s变量的引用,那将会非常惊讶.另一方面:
procedure AddObject(obj: TObject); // ... AddObject(TObject.Create);
AddObject保留引用并不奇怪,因为这个名称意味着它将参数添加到某个有状态存储中.该有状态存储是否为闭包形式是AddObject方法的实现细节.