我已经写了很多年的高级函数了,现在甚至已经写了很多模块。但有一个问题我一直没能找到答案。
让我们以Microsoft在MSMQ模块中提供的Cmdlet为例,并将其“重新实现”为高级PowerShell函数:Send-MsmqQueue
。但此函数与MSMQ模块提供的函数略有不同,因为它不仅接受$InputObject
参数的多个MSMQ队列,还接受$Name
参数的多个MSMQ队列名称,其中这两个参数属于不同的参数集。(此函数的Cmdlet版本通常只接受$Name
参数的单个字符串值。)我不会展示一个 * 完整 * 的重新实现,只是足以说明我有时会在出现这种情况时做什么。(注意:另一个细微的区别是,我将使用来自System.Messaging
命名空间的类,而不是PowerShell提供的Microsoft.Msmq.PowerShell.Commands
命名空间中的类。因此,假设在某处隐式地执行了Add-Type -AssemblyName System.Messaging
。)
function Send-MsmqQueue {
[CmdletBinding(DefaultParameterSetName = 'Name')]
[OutputType([Messaging.Message])]
Param (
[Parameter(
Mandatory,
ValueFromPipeline,
ParameterSetName = 'InputObject')
]
[Messaging.MessageQueue[]] $InputObject,
[Parameter(
Mandatory,
ValueFromPipeline,
ParameterSetName = 'Name')
]
[string[]] $Name,
# Below is the original parameter name, not mine ;)
[Messaging.Message] $MessageObject
# All other normal Send-MsmqQueue parameters elided as they are not
# needed to illustrate the premise of my question.
)
Process {
# When I have parameters defined as above, the first thing I do in my
# Process block is "homogenize" the data so I don't have to implement
# two foreach loops or do the branching on each foreach loop iteration
# which can obscure the main logic that is being executed, i.e., I get
# this done all "up-front".
#
# One aspect of my question is, from purely a PowerShell perspective,
# is this hurting performance in any meaningful way? (I know that when it
# comes to specific implementation details, there are INFINITE ways to
# write non-performant code, so from purely a PowerShell perspective,
# as far as the language design/inner-workings, is this hurting
# performance?
#
# NOTE: I don't normally need the wrapping "force this thing to be an
# array" construct (,<array_items>), BUT, in this case, the C#
# System.Messaging.MessageQueue class implements IEnumerable,
# which PowerShell (unhelpfully) iterates over automatically, and results
# in the messages in the queues being iterated over instead of the queues
# themselves, so this is an implementation detail specific to this
# particular function.
$Queues = (,@(
if ($PSCmdlet.ParameterSetName -ieq 'Name') {
# Handle when the parameter is NOT passed by the pipeline...
foreach ($n in $Name) { [Messaging.MessageQueue]::new($n) }
} else {
$InputObject
}
))
# I like using 'foreach (...) { ... }' instead of ForEach-Object because
# oftentimes, I will need to break or continue based on implementation
# details, and using ForEach-Object in combination with break/continue
# causes the pipeline to prematurely exit.
foreach ($q in $Queues) {
$q.Send($MessageObject)
# Normally, I wouldn't return this, especially since it wasn't
# modified, but this is a re-implementation of MSFT's Send-MsmqQueue,
# and it returns the sent message.
$MessageObject
}
}
}
字符串
正如我在这个问题的介绍中所述,我已经编写了许多函数,这些函数采用不同的基于集合的参数,这些参数属于不同的参数集,可以通过管道传输到函数中,这就是我使用的模式。我希望有人可以从PowerShell语言/风格的Angular 确认这是可以的,并且/或者帮助我理解为什么我不应该这样做,以及我应该考虑什么。
谢谢你,谢谢
2条答案
按热度按时间jv4diomz1#
一个基本的性能决策是您是否希望对参数传递进行优化,而不是 * 流水线输入*:
[string[]] $Name
)允许通过 argument(参数值)高效传递 * 多个 * 输入对象。String[]
,用于通过管道传递的数组的 * 每个 * 标量字符串元素:字符串
*注意:为简洁起见,上面的示例以及本答案中的所有其他示例都使用script block代替
function
定义。也就是说,函数声明(function foo { ... }
)后跟其调用(... | foo
)被缩短为功能等效的... | & { ... }
有关相关讨论,请参见GitHub issue #4242。
对于 array 参数,您确实需要确保自己进行逐个元素的处理,特别是在
process
块中,如果它们也是 * 管道绑定 * 的话。对于**“均质化”不同类型的参数值**,从而只需要 * 一个 * 处理循环,有两种基本的优化可能:
*仅声明 * 单个 * 参数,并依赖PowerShell将其他类型的值 * 自动 * 转换为该参数的类型,或实现自动应用的 * 自定义转换 *,从而完全消除了“同质化”的需要:
[string]
,如果该类型有一个静态::Parse()
方法,带有一个[string]
参数,则**转换是 * 自动的 *;例如:型
[Messaging.MessageQueue]
* 确实 * 有一个接受字符串的公共单参数构造函数(正如您的[Messaging.MessageQueue]::new($n)
调用所证明的那样),因此您可以简单地 * 省略 *$Name
参数声明,并依赖于[string]
输入的自动转换。[Foo[]] (0x2a, 43)
,见下文)和(很少使用的)内在.ForEach()
的类型转换形式(例如(0x2a, 43).ForEach([Foo])
)--在匹配构造函数的参数类型方面比调用单元素构造函数更严格。[double]
值,[Foo]::new(42.1)
成功(即自动执行到[int]
的转换),但[Foo] 42.1
和(42.1).ForEach([Foo])
都失败(后者目前产生模糊的错误消息)。ArgumentTransformationAttribute
类派生的自定义属性装饰参数;例如:型
型
型
,
数组构造函数(“逗号”)运算符,就像您的尝试一样,尽管:foreach
循环,以及@(...)
封装对象以 Package 在单元素数组中,因为@(...)
* 本身 * 将触发枚举。Write-Output -NoEnumerate ([Messaging.MessageQueue]::new($n))
,如Mathias的答案所示,也可以工作,但它 * 较慢 *。它归结为性能/简洁性与可读性/明确地用信号通知意图。[System.Messaging.MessageQueue]
示例 Package 在aux中。一元,
/的单元素 Package 器使用Write-Output -NoEnumerate
源于这样一个事实,即该类型实现了System.Collections.IEnumerable
接口,这意味着PowerShell默认情况下会自动 * 枚举 * 该类型的示例。[1]应用任何一种技术都可以确保[System.Messaging.MessageQueue]
作为一个整体 * 输出到管道(有关详细信息,请参阅this answer)。$Queues = [Messaging.MessageQueue[]] $Name
是 * 表达式 *,自动枚举 * 不 * 适用于它。[System.Messaging.MessageQueue]
示例或包含此类示例 * 的 * 单元素 * 数组,则需要使用相同的技术;例如:型
if
语句作为单个 * 赋值表达式 *($Queue = if ...
),而不是在if
语句的 branches 中赋值给$Queue
,您还可以防止$InputObject
受到不必要的枚举。[1]也有一些例外,特别是字符串和字典。有关详细信息,请参见this answer的底部部分。
pn9klfpd2#
这种模式(基于所选参数集“均匀化”输入实体)是完全有效的,并且至少在我个人看来构成了良好的参数设计。
也就是说,您可能希望使用
Write-Output -NoEnumerate
来避免笨拙的,@(...)
unwrapped-wrapped-array解包技巧:字符串