7

PowerShell 闭包似乎没有捕获函数的定义:

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { return { Write-host 'Calling x!'; x }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
new x

有什么方法可以捕获函数的定义吗?

我实际遇到的是我创建了一个闭包,但是当我的闭包被执行时,该函数不知何故超出了范围。(用于构建脚本的psake模块正在做一些奇怪的事情。)这样的事情:

PS C:\> function closure-maker () {
>>     function x() { Write-Host 'x!' }
>>
>>     return { Write-host 'Calling x'; x }.GetNewClosure()
>> }
>>
PS C:\> $y = closure-maker
PS C:\> & $y
Calling x
The term 'x' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:3 char:39
+     return { Write-host 'Calling x'; x <<<<  }.GetNewClosure()
    + CategoryInfo          : ObjectNotFound: (x:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

注意:使用 PowerShell 2.0,但如果有新的东西,对 3.0 答案感兴趣。

4

2 回答 2

5

好吧,我找到了至少适用于简单功能的东西。我们可以Get-Item用来获取描述函数的对象,然后从中提取原始脚本。像这样:

function x-caller-generator() {
    $xFunc = [ScriptBlock]::Create((Get-Item function:x).Definition)
    return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure()
}

如果从未重新定义函数(例如在我的函数超出范围的示例中),我们可以避免取消定义而直接使用函数对象:

function closure-maker () {
    function x() { Write-Host 'x!' }

    $xFunc = Get-Item function:x
    return { Write-host 'Calling x'; & $xFunc }.GetNewClosure()
}

如果在执行闭包之前重新定义函数(至少在与原始函数相同的范围内),则第二种方法将不起作用。物体显然是动态的;它跟踪当前定义。

我严重怀疑这是否适用于引用其他用户定义函数的函数,这些函数也可能超出范围,但我的用例不需要这样做。

样本输出:

创建脚本块

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { $xFunc = [ScriptBlock]::Create((Get-Item function:x).Definition); return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
original x

使用函数对象

PS C:\> function closure-maker () {
>>     function x() { Write-Host 'x!' }
>>
>>     $xFunc = Get-Item function:x
>>     return { Write-host 'Calling x'; & $xFunc }.GetNewClosure()
>> }
>>
PS C:\> $y = closure-maker
PS C:\> & $y
Calling x
x!

尝试在第一个示例中使用对象不起作用

PS C:\> function x() { Write-Host 'original x' }
PS C:\> function x-caller-generator() { $xFunc = Get-Item function:x; return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() }
PS C:\> $y = x-caller-generator
PS C:\> & $y
Calling x!
original x
PS C:\> function x() { Write-Host 'new x' }
PS C:\> & $y
Calling x!
new x
于 2013-05-25T03:15:23.843 回答
0

一个小的更正将使第一个示例工作:

clear
function x() { Write-Host 'original x' }
function x-caller-generator() { 
      $xFunc = $function:x; # instead of Get-Item
      return { Write-host 'Calling x!'; & $xFunc }.GetNewClosure() 
}
$y = x-caller-generator
& $y
function x() { Write-Host 'new x' }
& $y

输出:

Calling x!
original x
Calling x!
original x

PowerShell 有太多看起来相似但实际上行为不同的东西。您可以使用 $function 前缀获取函数对象。有人可能会认为它的工作方式与 Get-Item 相同,但它并不...

于 2016-06-06T10:02:33.110 回答