Why doesn't this work: $PSBoundParameters.CopyTo($boundParameters, 0)

This topic contains 2 replies, has 3 voices, and was last updated by Profile photo of Ben Lemmond Ben Lemmond 3 years, 6 months ago.

  • Author
    Posts
  • #11110
    Profile photo of Simon
    Simon
    Participant

    I'm trying to create a copy of $PSBoundParameters. I've got this:

    $boundParameters = [System.Collections.Generic.KeyValuePair[string,object][]]@()
    $PSBoundParameters.CopyTo($boundParameters, 0)

    but it produces this:

    Cannot find an overload for "CopyTo" and the argument count: "2".
    At C:\Users\Me\PowerShell\TEST.ps1:43 char:9
    + $PSBoundParameters.CopyTo($boundParameters, 0)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodCountCouldNotFindBest

    The attached screenshot, from ISE, suggests it should work. Is it me? Is a PowerShell funny?

  • #11114
    Profile photo of Dave Wyatt
    Dave Wyatt
    Moderator

    What is it that you want to accomplish with this code? You can simply do $boundParameters = $PSBoundParameters, but both variables will refer to the same Dictionary (technically, [System.Management.Automation.PSBoundParametersDictionary). Adding or removing keys / values from one dictionary would affect the other.

    Assuming you got the CopyTo function working, you'd have one object ($PSBoundParameters) that's still a dictionary, and one that's just an array of key/value pairs, but they'd still refer to the same Value objects. Changes made from one place or the other might affect both, depending on the types of objects stored in the dictionary.

    In any case, the reason you're having trouble is because that method is an explicit interface implementation, and working with those is a bit of a pain in PowerShell. I tried to get it working this morning, but haven't quite got there yet for some reason; here's my current test code:

    function Test
    {
        param (
            $FirstArg, $SecondArg, $ThirdArg
        )
    
        $boundParameters = New-Object -TypeName 'System.Collections.Generic.KeyValuePair`2[System.String,System.Object][]' -ArgumentList $PSBoundParameters.Count
        $boundParameters.GetType().FullName
    
        $type = [System.Collections.Generic.ICollection[System.Collections.Generic.KeyValuePair[String,Object]]]
        $method = $type.GetMethod('CopyTo')
    
        $method.GetParameters()
    
        $arguments = [Object[]]($boundParameters, 0)
        $method.Invoke($PSBoundParameters, $arguments)
    }
    
    Test 1 2 3
    

    You can find some more information on this topic at http://www.vistax64.com/powershell/169391-access-interfaces-members-net-object.html. Right now, I'm getting the error 'Exception calling "Invoke" with "2" argument(s): "Object of type 'System.Management.Automation.PSObject' cannot be converted to type 'System.Collections.Generic.KeyValuePair`2[System.String,System.Object][]'."' I'm not sure why, as I'm outputting the type of $boundParameters earlier in the function, and it's not a PSObject.

    As a workaround, you could just create the copy yourself, ie:

    function Test
    {
    param (
    $FirstArg, $SecondArg, $ThirdArg
    )

    $boundParameters = New-Object -TypeName 'System.Collections.Generic.KeyValuePair`2[System.String,System.Object][]' -ArgumentList $PSBoundParameters.Count

    $i = 0
    foreach ($pair in $PSBoundParameters.GetEnumerator())
    {
    $boundParameters[$i++] = $pair
    }

    $boundParameters
    }

    Test 1 2 3

  • #12097
    Profile photo of Ben Lemmond
    Ben Lemmond
    Participant

    I don't think CopyTo() will get you what you want. That will result in an array of KeyValuePair objects, i.e. not a lookup dictionary.
    If that is indeed what you want, Dave is correct about why it does not work. The Dictionary class implements the CopyTo method explicitly which basically makes it a private method. Therefore, it just needs to be cast to an ICollection. Also, the destination array needs to be allocated first.

    # Init destination array
    $boundParameters = @($null) * $PSBoundParameters.Count;
    # Cast to ICollection before calling CopyTo()
    ([System.Collections.ICollection]$PSBoundParameters).CopyTo($boundParameters, 0);

    However, I think this is probably what you want:
    $boundParameters = @{} + $PSBoundParameters;

    This will get you a copy of $PSBoundParameters with which you can add/remove items without affecting $PSBoundParameters.

You must be logged in to reply to this topic.