2010-03-10 11 views
6

Considerate questo codice:Nuova chiusura in 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 

ho pensato che quando creo nuova chiusura ({ write-host 'i: ' $i }.GetNewClosure()) valore $i sarà legato a questa chiusura. Ma non in questo caso. Dopo aver modificato il valore, write-host assume il nuovo valore.

D'altra parte, questo funziona:

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

Perché non funziona con il Register-ObjectEvent?

+0

sarebbe utile se è stato specificato quale risultato che vi aspettavate e che cosa risultato che abbiamo ottenuto. – Richard

+0

aggiunto uscite quindi spero sia chiaro .. – stej

+0

Sembra un bug per me o almeno dovremmo avere un modo per configurarlo. Ho trovato già inviato: https://connect.microsoft.com/PowerShell/feedback/details/541754/getnewclosure-doesnt-work-on-register-objectevent# –

risposta

2

I lavori vengono eseguiti in un modulo dinamico; i moduli hanno un sessionstate isolato e condividono l'accesso ai globals. Le chiusure PowerShell funzionano solo all'interno della stessa catena sessione/ambito. Fastidioso, sì.

-Oisin

p.s. Dico "posti di lavoro", perché i gestori di eventi sono posti di lavoro in modo efficace locali, non è diverso da script eseguito con start-lavoro (solo computer locale, implicita, non usando -Computer localhost)

+0

Ha senso, accettato. Come hai trovato informazioni su posti di lavoro e gestori di eventi? Un'altra fonte di Reflector? :) – stej

+1

No, non ho il codice sorgente. Solo lo staff di Microsoft lo ha. I gestori di eventi sono lavori, perché il lavoro di ritorno li restituirà. – x0n

0

Penso che tu stia facendo ipotesi che non valgono. PSH è interpretato, quindi quando viene creato un blocco di codice, contiene solo il codice sorgente. Quando verrà valutato successivamente, tutte le variabili utilizzate verranno esaminate nel normale modo PSH: prima nell'attuale ambito e quindi in ogni ambito esterno fino a quando non viene trovata una variabile con un nome corrispondente.

Quando il timer attiva il suo evento, esegue il blocco di codice e quindi cerca $i. Che si trova nel perimetro esterno, con un valore pari a 2.

Nel secondo caso, se si utilizza direttamente il blocco di codice (rimuovere chiamata a GetNewClosure) poi la seconda esecuzione dà 2.

+0

Ed è quello che voglio - nel secondo esempio voglio ottenere 1, non 2. Funziona lì, ma non nel primo esempio. – stej

+0

Si suppone che GetNewClosure cambi il comportamento che si menziona nel primo paragrafo. – Segfault

0

utilizzare le variabili globali in questo 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 

Fonte:

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