I asked this question over on StackOverflow and received an answer there.
Basically, yes, the pattern that I use above is a good pattern.
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
I've been writing advanced functions for many years now and have even written quite a few modules at this point. But, there's one question for which I have never really been able to find an answer.
Let's look at a Cmdlet that Microsoft provides in the MSMQ module, and "re-implement" it as an advanced PowerShell function: Send-MsmqQueue. But this function will be a bit different than the one that's provided with the MSMQ module in that not only will it accept multiple MSMQ queues for the $InputObject parameter, but also multiple MSMQ queue names for the $Name parameter (the Cmdlet version only accepts a single string for the $Name parameter). I won't be showing the entire implementation, just enough to illustrate my question.
function Send-MsmqQueue {
[CmdletBinding(DefaultParameterSetName = 'Name')]
[OutputType([Microsoft.Msmq.PowerShell.Commands.Message])]
Param (
# I'm going to use native C# System.Messaging objects here, instead of the
# PowerShell ones, in order to better demonstrate my question. So, somewhere,
# Add-Type -AssemblyName System.Messaging will need to be executed....
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObject')]
[Messaging.MessageQueue[]] $InputObject,
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Name')]
[string[]] $Name,
[Messaging.Message] $MessageObject
# All other parameters are elided, as this is the bare minimum set
# necessary to demonstrate my question
)
Process {
# I do this to "homogenize" the data. If I'm sent a string, I get the queue;
# Otherwise, I just return the InputObject. And, this handles when the parameter
# is passed as an array and not via the pipeline.
#
# I do it this way so that I don't have to basically implement the core logic
# of the function TWICE based on the parameter set used. I always operate over
# (in this case) a queue.
#
# The biggest potential problem I see with this is the foreach loop for processing
# potentially `x` names, but I'm not sure how "big" an issue that really is. At most,
# it makes this function O(2n) instead of O(1n), which is still O(n) since constants
# are ignored in Big-O notation.
#
# NOTE: Normally, I don't do the (,<stuff>) wrapper as shown here, but C#
# Messaging.MessageQueue objects implement IEnumerable, and I want to enumerate over
# the queues themselves and not the messages in the queues! So, just ignore the (,...).
$Queues = (,@(
if ($PSCmdlet.ParameterSetName -ieq 'Name') {
foreach ($n in $Name) { [Messaging.MessageQueue]::new($n) }
} else {
$InputObject
}
))
# I like using foreach (...) { ... } instead of ForEach-Object, because oftentimes,
# I may need to break or continue within, and using ForEach-Object causes issues
# due to the way break and continue operate inside a ForEach-Object scriptblock.
foreach ($q in $Queues) {
$q.Send($MessageObject)
$MessageObject
}
}
}
I have written many functions which take varying types of pipelined input collections like the one shown above. Is what I'm doing above advisable? Is there a different way that this should be done?
This answer has been deleted due to a violation of our Code of Conduct. The answer was manually reported or identified through automated detection before action was taken. Please refer to our Code of Conduct for more information.
1 deleted commentComments have been turned off. Learn more
I asked this question over on StackOverflow and received an answer there.
Basically, yes, the pattern that I use above is a good pattern.
Sorry, this originally was added as a "comment", then saw the "Post Your Answer"--but then, both seem to do the same thing?? Ugh, MSFT still can't create a basic, intuitive Q&A site... Don't know why I even bothered.
Another way to do it (without the -NoEnumerate switch) is to rearrange the code a bit:
function Send-MsmqQueue {
[CmdletBinding(DefaultParameterSetName = 'Name')]
# [OutputType([Microsoft.Msmq.PowerShell.Commands.Message])]
Param (
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'InputObject')]
[array[]] $InputObject,
[Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Name')]
[string[]] $Name,
[array] $MessageObject
)
Process {
[array]$Queues= @()
if ($PSCmdlet.ParameterSetName -ieq 'InputObject'){
$Queues = $InputObject
}
else{
foreach ($n in $Name){
$Queues += [PSCustomObject]@{Stuff = $n}
}
}
foreach ($q in $Queues) {
$q
}
}
}
$x = Send-MsmqQueue -Name 1,2,3
$y = Send-MsmqQueue -InputObject ([PSCustomObject]@{Stuff='A'},[PSCustomObject]@{Stuff='B'},[PSCustomObject]@{Stuff='C'})
I didn't use any of the actual message queuing objects, just strings and PSCustomObjects. The concept is there, though.