问题
This Q&A established that the powershell pipeline unrolls some collections sometimes. Suppose we have a function that emits a collection that might get unrolled, but we don't want the pipeline to do any unrolling. Here is an example demonstrating the unwanted unrolling:
Function EmitStack{
[Cmdletbinding()]
param()
process{[System.Collections.Stack]@(10,20,30)}
}
$stack = [System.Collections.Stack]@(10,20,30)
$stack.GetType()
$EmittedStack = EmitStack
$EmittedStack.GetType()
#Name BaseType
#---- --------
#Stack System.Object
#Object[] System.Array
What we want is the $EmittedStack
variable to contain the unadulterated, untouched [System.Collections.Stack]
object that the function puts in the pipeline. Instead, it contains the unrolled items of the stack, re-rolled into an array. Is there a way for the caller to get the original object without subjecting it to powershell's pipeline unrolling (and re-rolling)?
Clarification: This question is about whether the caller can get the unadulterated object even if it hasn't been wrapped in a sacrificial array by the function before it puts it in the pipeline. In other words, I'm looking for a solution that doesn't involve changing the function.
Reason: Which types are unrolled is not clearly defined in powershell which implies a higher degree of unpredictability from system to system, powershell-version to powershell-version, dotnet-version to dotnet-version, etc. In case, for example, some powershell environments' pipelines unexpectedly unroll [System.Collections.Generic.Dictionary]
(remember there are no documented rules for this) it would help for the caller to be able to compensate as needed without having to dig into the callee code.
回答1:
Powershell only unrolls once. The standard technique therefore is to capture the result in a one-item array, which will get unrolled and give you back the original object:
Function EmitStack{
[Cmdletbinding()]
param()
process{(,[System.Collections.Stack]@(10,20,30))}
}
Note the (,item)
trick.
Edit: there is no way for a caller to get the original object after unrolling -- Powershell doesn't maintain some sort of special pseudo-variable that contains the content before it was unrolled. After unrolling, it would be indistinguishable from a regular enumerable. Although you could devise solutions that bypass the pipeline altogether (pass a parameters and assign a property, or maybe get very creative with a closure) changing the function would be unavoidable.
As @Matt points out, if the caller knows what type it's supposed to get back (or rather, if the caller wants to ensure it's always a specific type) they could just always convert the result to their desired type, so it doesn't matter if the result is unrolled or not. This still doesn't get you back the original object if unrolling did take place (but probably a very serviceable copy), and it assumes that Powershell can always convert to a type if it knows how to unroll objects of that type, which is probably not universally true.
来源:https://stackoverflow.com/questions/28724537/is-there-a-way-for-a-caller-to-get-the-output-of-a-powershell-function-without-s