2010-03-10 30 views
6

新封考虑以下代码:上脚本块

PS> $timer = New-Object Timers.Timer 
PS> $timer.Interval = 1000 
PS> $i = 1; 
PS> Register-ObjectEvent $timer Elapsed -Action { write-host 'i: ' $i }.GetNewClosure() 
PS> $timer.Enabled = 1 
i: 1 
i: 1 
i: 1 
... 
# wait a couple of seconds and change $i 
PS> $i = 2 
i: 2 
i: 2 
i: 2 

我认为,当我创建新的闭包({ write-host 'i: ' $i }.GetNewClosure())的$i值将被捆绑到这个封闭。但不是在这种情况下。如果我更改该值,则write-host将采用新值。

在另一边,这个工程:

PS> $i = 1; 
PS> $action = { write-host 'i: ' $i }.GetNewClosure() 
PS> &$action 
i: 1 
PS> $i = 2 
PS> &$action 
i: 1 

为什么不与Register-ObjectEvent工作?

+0

如果你指定你所期望的结果,什么导致你有这将有助于。 – Richard 2010-03-10 09:58:26

+0

添加输出,所以我希望它很清楚.. – stej 2010-03-10 10:21:23

+0

它看起来像一个错误,或者至少我们应该有一种方法来配置它。我发现它已经提交: https://connect.microsoft.com/PowerShell/feedback/details/541754/getnewclosure-doesnt-work-on-register-objectevent# – 2010-05-04 11:33:41

回答

2

作业被执行在一个动态模块中;模块已隔离sessionstate,并共享全局变量。 PowerShell闭包只能在同一个sessionstate/scope链中工作。烦人,是的。

-Oisin

p.s.我说的“工作”,因为事件处理程序是有效的地方工作,没有比脚本不同的是与启动工作(本地计算机只,含蓄,不使用本地主机 - 计算机)

+0

有道理,接受。你是如何找到有关工作和事件处理程序的信息的?其他来源比反射? :) – stej 2010-06-30 05:56:57

+1

不,我没有源代码。只有微软员工才有。事件处理程序是作业,因为get-job会返回它们。 – x0n 2010-06-30 15:31:43

0

我认为你正在做出不成立的假设。 PSH被解释,所以当一个代码块被创建时它只是保存源代码。在稍后评估它使用的任何变量时,将以普通PSH方式查找:首先在当前范围内,然后在每个外部范围内查找,直到找到具有匹配名称的变量。

当定时器触发它的事件,则执行代码块,因此查找$i。这是在外部范围发现的2

的值。在第二种情况下,如果只是直接使用码块(删除调用GetNewClosure),那么第二执行给出2.

+0

这就是我想要的 - 在第二个例子中,我想得到1,而不是2.它在那里工作,但不在第一个例子中。 – stej 2010-03-10 10:25:59

+0

GetNewClosure应该改变你在第一段中提到的行为。 – Segfault 2010-03-10 10:28:43

0

使用在这种情况下,全局变量运行:

PS> $global:i = 1 
PS> $timer = New-Object Timers.Timer 
PS> $timer.Interval = 1000 
PS> Register-ObjectEvent $timer Elapsed -Action { write-host 'i: ' $global:i }.GetNewClosure() 
PS> $timer.Enabled = 1 
i: 1 
i: 1 
i: 1 

PS> Set-Variable -Name i -Value 2 -Scope Global 

i: 2 
i: 2 
i: 2 

来源:

http://stackoverflow.com/q/12535419/1287856