匿名方法范围 [英] Scope of anonymous methods

查看:186
本文介绍了匿名方法范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

匿名方法的一个好处是我可以使用调用上下文中本地的变量。有没有什么理由为什么这不适用于out-parameters和函数结果?

  function ReturnTwoStrings(out Str1:String):串; 
begin
ExecuteProcedure(procedure
begin
Str1:='First String';
Result:='Second String';
end)
结束

当然是非常人为的例子,但我遇到了一些这样会有用的情况。 p>

当我尝试编译时,编译器抱怨说他无法捕获符号。另外,当我尝试这样做时,我有一个内部错误。



编辑我只是意识到它适用于正常的参数,如



  ...(列表:TList)

这不是其他情况吗?谁保证在执行匿名方法时引用仍然指向一个活动对象?

解决方案

无法捕获变量输出参数和结果变量,因为无法静态验证此操作的安全性。当Result变量为托管类型(例如字符串或接口)时,存储器实际上由调用者分配,并且对该存储的引用作为隐式参数传递;换句话说,结果变量取决于它的类型,就像一个out参数。



由于Jon提到的原因,安全性无法验证。由匿名方法创建的关闭可以超过创建它的方法激活,并且可以类似地超出调用其创建方法的方法的激活。因此,捕获的任何var或out参数或Result变量可能会最终成为孤立的,并且将来封锁内的任何写入都会损坏堆栈。



当然,Delphi不会在托管环境中运行,并且它没有与例如相同的安全限制C#。语言可以让你做你想要的。但是,在出错的情况下,会很难诊断错误。不良行为将显示为常规变化值中的局部变量,没有可见的近似原因;如果从另一个线程调用方法引用,将会更糟糕。



这将很难调试。即使硬件内存断点将是一个相对较差的工具,因为堆栈经常被修改。有人需要在触发另一个断点时有条件地打开硬件内存断点(例如,在方法输入时)。 Delphi调试器可以做到这一点,但是我猜测大多数人都不了解这种技术。



更新:尊重对于您的问题的补充,通过值传递实例引用的语义在包含闭包的方法(捕获paramete0和不包含闭包的方法)的方法之间几乎不同,任一方法可以保留对传递的参数的引用值;不捕获参数的方法可以简单地将引用添加到列表中,或将其存储在私有字段中。



与引用传递的参数不同,因为程序员这样做:

 程序GetSomeString(out s:string); 
/ / ...
GetSomeString(s);

如果GetSomeString为请保留对 s 变量的引用。另一方面:

  procedure AddObject(obj:TObject); 
// ...
AddObject(TObject.Create);

这个并不奇怪, AddObject 引用,因为这个名字意味着它将参数添加到一些状态存储。这个有状态商店是否以闭包的形式是 AddObject 方法的实现细节。


One nice thing about anonymous methods is that I can use variables that are local in the calling context. Is there any reason why this does not work for out-parameters and function results?

function ReturnTwoStrings (out Str1 : String) : String;
begin
  ExecuteProcedure (procedure
                    begin
                      Str1 := 'First String';
                      Result := 'Second String';
                    end);
end;

Very artificial example of course, but I ran into some situations where this would have been useful.

When I try to compile this, the compiler complains that he "cannot capture symbols". Also, I got an internal error once when I tried to do this.

EDIT I just realized that it works for normal parameters like

... (List : TList)

Isn't that as problematic as the other cases? Who guarantees that the reference is still pointing to an alive object whenever the anonymous method is executed?

解决方案

Var and out parameters and the Result variable cannot be captured because the safety of this operation cannot be statically verified. When the Result variable is of a managed type, such as a string or an interface, the storage is actually allocated by the caller and a reference to this storage is passed as an implicit parameter; in other words, the Result variable, depending on its type, is just like an out parameter.

The safety cannot be verified for the reason Jon mentioned. The closure created by an anonymous method can outlive the method activation where it was created, and can similarly outlive the activation of the method that called the method where it was created. Thus, any var or out parameters or Result variables captured could end up orphaned, and any writes to them from inside the closure in the future would corrupt the stack.

Of course, Delphi does not run in a managed environment, and it doesn't have the same safety restrictions as e.g. C#. The language could let you do what you want. However, it would result in hard to diagnose bugs in situations where it went wrong. The bad behaviour would manifest itself as local variables in a routine changing value with no visible proximate cause; it would be even worse if the method reference were called from another thread.

This would be fairly hard to debug. Even hardware memory breakpoints would be a relatively poor tool, as the stack is modified frequently. One would need to turn on the hardware memory breakpoints conditionally upon hitting another breakpoint (e.g. upon method entry). The Delphi debugger can do this, but I would hazard a guess that most people don't know about the technique.

Update: With respect to the additions to your question, the semantics of passing instance references by value is little different between methods that contain a closure (and capture the paramete0 and methods that don't contain a closure. Either method may retain a reference to the argument passed by value; methods not capturing the parameter may simply add the reference to a list, or store it in a private field.

The situation is different with parameters passed by reference because the expectations of the caller are different. A programmer doing this:

procedure GetSomeString(out s: string);
// ...
GetSomeString(s);

would be extremely surprised if GetSomeString were to keep a reference to the s variable passed in. On the other hand:

procedure AddObject(obj: TObject);
// ...
AddObject(TObject.Create);

It is not surprising that AddObject keeps a reference, since the very name implies that it's adding the parameter to some stateful store. Whether that stateful store is in the form of a closure or not is an implementation detail of the AddObject method.

这篇关于匿名方法范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆