1

My functions looks like following:

function GeneratePermutations() {
    Param(
       [System.Collections.ArrayList]$Lists,
       [ref][System.Collections.ArrayList]$result,
       [Int]$depth,
       [String]$current
    )

    if ($depth -eq $Lists.Count)
    {
       $result.Value.Add($current);
    }
    else
    {
        for ($i = 0; $i -lt $Lists[$depth].Count; $i = $i + 1)
        {
            GeneratePermutations $values $result ($depth + 1) ($current + $Lists[$depth][$i])
            # tried as well:
            # GeneratePermutations $values [ref]($result.Value) ($depth + 1) ($current + $Lists[$depth][$i])
        }
    }
}

And I try to use it like following:

$x = New-Object System.Collections.ArrayList
GeneratePermutations $values [ref]($x) 0 ""

I get following exception (in german):

System.Management.Automation.ParameterBindingArgumentTransformationException: Die
Argumenttransformation für den Parameter "result" kann nicht verarbeitet werden. Der Wert "[ref]" vom Typ
"System.String" kann nicht in den Typ "System.Collections.ArrayList" konvertiert werden. --->
System.Management.Automation.ArgumentTransformationMetadataException: Der Wert "[ref]" vom Typ "System.String" kann
nicht in den Typ "System.Collections.ArrayList" konvertiert werden. --->
System.Management.Automation.PSInvalidCastException: Der Wert "[ref]" vom Typ "System.String" kann nicht in den Typ
"System.Collections.ArrayList" konvertiert werden.
   bei System.Management.Automation.LanguagePrimitives.ThrowInvalidCastException(Object valueToConvert, Type
resultType)
   bei System.Management.Automation.LanguagePrimitives.ConvertNoConversion(Object valueToConvert, Type resultType,
Boolean recurse, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
   bei System.Management.Automation.LanguagePrimitives.ConversionData`1.Invoke(Object valueToConvert, Type resultType,
Boolean recurse, PSObject originalValueToConvert, IFormatProvider formatProvider, TypeTable backupTable)
   bei System.Management.Automation.LanguagePrimitives.ConvertTo(Object valueToConvert, Type resultType, Boolean
recursion, IFormatProvider formatProvider, TypeTable backupTypeTable)
   bei System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object
inputData, Boolean bindingParameters, Boolean bindingScriptCmdlet)
   --- Ende der internen Ausnahmestapelüberwachung ---
   bei System.Management.Automation.ArgumentTypeConverterAttribute.Transform(EngineIntrinsics engineIntrinsics, Object
inputData, Boolean bindingParameters, Boolean bindingScriptCmdlet)
   bei System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter,
CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
   --- Ende der internen Ausnahmestapelüberwachung ---
   bei System.Management.Automation.ExceptionHandlingOps.CheckActionPreference(FunctionContext funcContext, Exception
exception)
   bei System.Management.Automation.Interpreter.ActionCallInstruction`2.Run(InterpretedFrame frame)
   bei System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
   bei System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
In Zeile:1 Zeichen:1
+ .\process.ps1
+ ~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,process.ps1

I think I have a problem with recursively reusing the result array, can someone tell me how to solve the issue? It seems like powershell wants to convert the result array to string which it apperently can't do, but why does it try to convert it to a string?

2 Answers 2

1

I had the same problems with passing parameters by reference in a recursive function and came to the conclusion that the [Ref] type is setup in PowerShell doesn't add much value if you compare it to e.g. the VBScript ByRef argument.

I found to ways to workaround this:

Put your variable in a HashTable (or PSCustomObject), @{Var = $Result} which properties will be by reference. In your case, I would probably simply use a HashTable rather then an array:

function GeneratePermutations() {
    Param(
       [System.Collections.ArrayList]$Lists,
       [HashTable]$result = @{},
       [Int]$depth,
       [String]$current
    )

    if ($depth -eq $Lists.Count)
    {
       $result.$current = $True;
    }
    else
    {
        for ($i = 0; $i -lt $Lists[$depth].Count; $i = $i + 1)
        {
            GeneratePermutations $values $result ($depth + 1) ($current + $Lists[$depth][$i])
        }
    }
}

Or remove the concerned $Result variable from the parameter set and create a variable which write scope is from the root of the recursive function (and doesn't overwrite a possible already existing $Result variable:

If (@(Get-PSCallStack)[1].Command -ne $MyInvocation.MyCommand.Name) {
    New-Variable -Name Result -Option AllScope -Value @()
}
if ($depth -eq $Lists.Count)
    {
        $result.Add($current);
        ...
Sign up to request clarification or add additional context in comments.

2 Comments

Could solve it with the root variable, still waiting if someone can solve the issue with the ref solution. Thanks
I make a small change to the answer, if you use a "root" variable, then you should add the items of cause directly: $result.Add($current); (not $result.Value.Add($current);), and the same counts for the first workaround, which I have changed as wel to: $result.$current = $True;
0

The following syntax works:

GeneratePermutations $values ([ref]$x) 0 ""

Think there is some operator precedence in play, see below.

[PS]> [ref]$x

Value
-----
{, , , ...}

[PS]> [ref]$x.gettype()
Value
-----
System.Collections.ArrayList

[PS]> ([ref]$x).gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
False    False    PSReference`1                            System.Management.Automation.PSReference

In the first instance we get a textual representation of the reference, it's Value property is the object.

In the second, we see that we still get a reference, but now it's Value is System.Collections.ArrayList which is actually a string. Could be that when passing between the functions there is some reflection going on as the types are being processed, or something is calling ToString() and the function ends up with this string reference, causing the error you saw.

In the third example, you can see that the proper type has been returned, and when passed to your function like this the error disappears and everything behaves as expected.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.