A problémás kód leegyszerűsítve:
function foo {
param ( [ScriptBlock] $Callback )
'foo'
& $Callback
}
function bar {
param ( [ScriptBlock] $Callback )
'bar'
foo {
'inner script block'
& $Callback
}
}
Kimenet:
PS> foo { 'outer script block' }
foo
test
PS> bar { 'outer script block' }
bar
foo
inner script block
inner script block
inner script block
Vagyis bar
végtelen rekurzióra fut, mert $Callback
értéke a foo
-nak átadott paraméter lesz, nem pedig a bar
-nak átadott, azaz a script block foo
-ban önmagát hívja. Nem igazán értettem ezt a viselkedést, mégis hogy láthatja a bar
-ban definiált script block a még meg sem hívott foo
paraméterét?
A dokumentációból kiderült, hogy a függvények és script blockok egyaránt saját scope-ban futnak, ahogy arra számítottam:
Functions normally create a scope. The items created in a function, such as variables, exist only in the function scope.
Like Invoke-Command, the call operator executes the script block in a child scope.
Első ránézésre a PowerShell scope-jai is hasonlóan működnek mint más nyelveken.
Mivel a dokumentációból nem lettem okosabb, írtam pár tesztet.
Az első 3 teszt egybevág a dokumentációban leírtakkal, azaz a parent scope változói a child scopeban láthatóak, de nem módosíthatóak, hanem eltakarhatóak. Azonban a negyedik teszt már meglepő, ezek szerint nem a függvénydefiníció helye számít, hanem a hívás helye. Ugyanez igaz a script blockokra is, ez a második 4 teszt.
Ennek a viselkedésnek az elkerülésére több megoldás is lehetséges, ezekből kettőt próbáltam ki.
Az egyik megoldás, hogy a script blockból valódi closure-t lehet csinálni a GetNewClosure()
metódussal. Ezzel a bar
függvény így módosul:
function bar {
param ( [ScriptBlock] $Callback )
'bar'
foo {
'inner script block'
& $Callback
}.GetNewClosure()
}
Kimenet:
PS> bar { 'outer script block' }
bar
foo
inner script block
outer script block
A másik megoldás a private
módosító használata. Ezzel foo
függvény így módosul:
function foo {
param ( [ScriptBlock] $Private:Callback )
'foo'
& $Callback
}
Kimenet:
PS> bar { 'outer script block' }
bar
foo
inner script block
outer script block
A tanulság az, hogy minden nyelvnek vannak elsőre meglepő tulajdonságai, ezért soha nem szabad azt feltételezni, hogy egy nyelv adott funkcionalitása pontosan ugyanúgy működik mint más nyelvek hasonló funkcionalitása. A meglepő viselkedésnek érdemes utánajárni, kipróbálni, hogy legközelebb már ne legyen min meglepődni. Szerencsés esetben olyan technikákra is rá lehet lelni, amik más problémák megoldásában is segíthetnek.
- BaT blogja
- A hozzászóláshoz be kell jelentkezni
Hozzászólások
sub
- A hozzászóláshoz be kell jelentkezni