Рассмотрите этот код:
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
будет связан с этим закрытием. Но не в этом случае. Afer я изменяю значение, 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
?
Задания выполняются в динамическом модуле; модули имеют изолированное состояние сеанса и имеют общий доступ к глобальным объектам. Замыкания PowerShell работают только в той же цепочке состояния сеанса / области видимости. Раздражает, да.
-Oisin
шт. Я говорю «задания», потому что обработчики событий фактически являются локальными заданиями, ничем не отличными от сценария, запускаемого с помощью start-job (только локальный компьютер, неявно, без использования -computer localhost)
Я думаю, вы делаете предположения, которые не соответствуют действительности. PSH интерпретируется, поэтому когда создается блок кода, он просто содержит исходный код. При последующей оценке любые переменные, которые он использует, будут искаться обычным для PSH способом: сначала в текущей области видимости, а затем в каждой внешней области видимости, пока не будет найдена переменная с подходящим именем.
Когда таймер запускает свое событие, он выполняет блок кода и таким образом ищет $i
.
Во втором случае, если просто использовать блок кода напрямую (удалить вызов GetNewClosure
), то второе выполнение дает 2.