PowerShell 通用列表 .Where() 从更高范围进行变量扩展的性能

问题描述 投票:0回答:1

给定一个名为

$derivedValues
的哈希表和一个需要变量扩展的变量,我看到了一些有趣的性能问题。 给定:

$rule.opnd1 = 'uninstallString'
$rule.opnd2 = 'MsiExec.exe /X$($_.keyGUID)'
$filteredKeys.Where({$_.($rule.opnd1) -eq $ExecutionContext.InvokeCommand.ExpandString($rule.opnd2)})

我取得了良好的表现。千分之几秒。但是当要展开的变量不是

$_
比如

$rule.opnd1 = 'displayName'
$rule.opnd2 = '$($derivedValues.displayName)'

性能真的很差。轻松接近半秒。我假设它与引用“父”范围内的

$derivedValues
相关,如果你愿意的话,与按定义在闭包范围内定义的
$_
相比。

我想知道的是,有没有办法将

$derivedValues
变量实际传递到闭包中,以便变量扩展始终是本地范围?如果是这样,这可能会提高性能吗?我知道这是一个奇怪的场景,所以如果有一种方法,但没有人知道它是否会有所帮助,因为没有人需要做这样的事情,那么,我会报告回来。 :)

powershell closures variable-expansion
1个回答
0
投票

您可以在此处使用

ScriptBlock.InvokeWithContext
传入要由比较表达式脚本块求值的变量。

之前分享了一个示例来展示它如何使用示例对象工作:

# supposing this object is `$_`
$inputObject = [pscustomobject]@{ displayName = 'test' }
# this object would be the value of `opnd2`
$derivedValues = [pscustomobject]@{ displayName = 'test' }
# example object of what `$rule` would be
$rule = [pscustomobject]@{
    opnd1 = 'displayName'
    opnd2 = '$derivedValues.displayName'
}
# define the variables passed-in to the expression
$variables = [System.Collections.Generic.List[psvariable]]@(
    [psvariable]::new('_', $inputObject)
    [psvariable]::new('rule', $rule)
    [psvariable]::new('derivedValues', $derivedValues))
# define the comparison expression
$expression = { $_.($rule.opnd1) -eq $derivedValues.displayName }
# evaluate it with the context
$expression.InvokeWithContext($null, $variables)[0]

上面显示,比较

$_.($rule.opnd1) -eq $derivedValues.displayName
与传入变量的计算结果为
true

进一步提高性能的下一步是使用带有

foreach
条件的
if
循环,而不是本身非常慢的
.Where
。使用
foreach
循环,我将在本示例中使用
$_
,而不是将
$key
传递到上下文:

# supposing this object is `$filteredKeys`
$filteredKeys = 0..10 | ForEach-Object { [pscustomobject]@{ displayName = $_ } }
# this object would be the value of `opnd2`
$derivedValues = [pscustomobject]@{ displayName = 5 }
# example object of what `$rule` would be
$rule = [pscustomobject]@{
    opnd1 = 'displayName'
    opnd2 = '$derivedValues.displayName'
}
# define the variables passed-in to the expression
$variables = [System.Collections.Generic.List[psvariable]]@(
    [psvariable]::new('key', $null)
    [psvariable]::new('rule', $rule)
    [psvariable]::new('derivedValues', $derivedValues))
# define the comparison expression
$expression = { $key.($rule.opnd1) -eq $derivedValues.displayName }

foreach ($key in $filteredKeys) {
    # Gets the first `psvariable` and sets the value to the current item in the loop
    $variables[0].Value = $key
    if ($expression.InvokeWithContext($null, $variables)[0]) {
        $key
    }
}

# Outputs:
#
# displayName
# -----------
#           5
© www.soinside.com 2019 - 2024. All rights reserved.