2010-03-10 21 views
6

Considere este código:Nuevo cierre en ScriptBlock

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 

que supone que cuando se crea nuevo cierre ({ write-host 'i: ' $i }.GetNewClosure()) valor de $i estará ligada a este cierre. Pero no en este caso. Después de cambiar el valor, write-host toma el nuevo valor.

Por otro lado, esto funciona:

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

Por qué no funciona con el Register-ObjectEvent?

+0

sería de gran ayuda si ha especificado qué resultado que se esperaba y qué resultado que tienes. – Richard

+0

resultados agregados, así que espero que esté claro .. – stej

+0

Parece un error para mí o al menos deberíamos tener una forma de configurar esto. Ya lo encontré presentado: https://connect.microsoft.com/PowerShell/feedback/details/541754/getnewclosure-doesnt-work-on-register-objectevent# –

Respuesta

2

Los trabajos son ejecutados en un módulo dinámico; los módulos tienen sessionstate aislado y comparten acceso a globales. Los cierres de PowerShell solo funcionan dentro de la misma cadena sessionstate/scope. Molesto, sí

-Oisin

p.s. Digo "puestos de trabajo", porque los controladores de eventos son puestos de trabajo de manera efectiva locales, no es diferente de la escritura que se ejecutan con la puesta en trabajo (máquina local solamente, implícita, no usar -computer localhost)

+0

Tiene sentido, aceptado. ¿Cómo encontraste esa información sobre trabajos y manejadores de eventos? ¿Otra fuente que Reflector? :) – stej

+1

No, no tengo el código fuente. Solo el personal de microsoft tiene eso. Los manejadores de eventos son trabajos, porque get-job los devolverá. – x0n

0

Creo que está haciendo suposiciones que no se cumplen. PSH se interpreta, por lo tanto, cuando se crea un bloque de código, solo contiene el código fuente. Cuando se evalúe posteriormente, cualquier variable que use se buscará de la manera normal de PSH: primero en el alcance actual y luego en cada ámbito externo hasta que se encuentre una variable con un nombre coincidente.

Cuando el temporizador dispara su evento, ejecuta el bloque de código y busca $i. Que se encuentra en el perímetro exterior con un valor de 2.

En el segundo caso, si sólo se utiliza el bloque de código directamente (quitar llamada a GetNewClosure), entonces la segunda ejecución da 2.

+0

Y eso es lo que quiero, en el segundo ejemplo quiero obtener 1, no 2. Funciona allí, pero no en el primer ejemplo. – stej

+0

Se supone que GetNewClosure cambia el comportamiento que menciona en su primer párrafo. – Segfault

0

utilizar variables globales en ese caso:

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 

Fuente:

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