GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — A ValueFromRemainingArguments parameter by default invariably receives a [List[object]] collection rather than a scalar / PowerShell array ([object[]]), as appropriate · Issue #4625 · PowerShell/PowerShell

A ValueFromRemainingArguments parameter by default invariably receives a [List[object]] collection rather than a scalar / PowerShell array ([object[]]), as appropriate #4625

New issue
Copy link
New issue
Copy link
Closed
Closed
A ValueFromRemainingArguments parameter by default invariably receives a [List[object]] collection rather than a scalar / PowerShell array ([object[]]), as appropriate#4625
Copy link
Labels
Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifWG-Languageparser, language semanticsparser, language semantics

Description

@mklement0
Michael Klement (mklement0)
opened on Aug 20, 2017
Issue body actions

Related: #3773 and #3154

Declaring a (at most 1) parameter with attribute ValueFromRemainingArguments allows you to collect any remaining arguments - those not bound to other parameters - in a single parameter.

Given that there may be 1 or more remaining arguments, that would usually imply:

  • bind a scalar to the parameter if there's only one remaining argument
  • bind a regular PowerShell array ([object[]]), if there are multiple remaining arguments.

By contrast, a [List[object]] instance is currently bound, irrespective of whether 1 or more remaining arguments are bound.

Steps to reproduce

Create script t.ps1 in the current dir. with the following content:

[CmdletBinding()]
param(
  [Parameter(ValueFromRemainingArguments)]
  $Rest
)
$Rest.Count
$Rest.GetType().FullName

Then call:

./t foo
./t foo bar

Expected behavior

1
System.String
2
System.Object[]

Actual behavior

1
System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]
2
System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]]

Environment data

PowerShell Core v6.0.0-beta.5 on macOS 10.12.6
PowerShell Core v6.0.0-beta.5 on Ubuntu 16.04.3 LTS
PowerShell Core v6.0.0-beta.5 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.483 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)

Activity

  1. Michael Klement (mklement0)
    changed the title [-]ValueFromRemainingArguments parameters by default invariably receive a [List[object]] collection rather than a scalar / PowerShell array ([object[]]), as appropriate[/-] [+]A ValueFromRemainingArguments parameter by default invariably receives a [List[object]] collection rather than a scalar / PowerShell array ([object[]]), as appropriate[/+] on Aug 20, 2017
  2. lzybkr commented on Aug 21, 2017

    @lzybkr
    Jason Shirk (lzybkr)
    on Aug 21, 2017
    Contributor
    More actions

    Parameters that use ValueFromRemainingArguments usually specify an array type, but if the type is omitted, isn't it more useful to pass a collection? The command probably expects a collection anyway.

    This would also be a breaking change.

  3. mklement0 commented on Aug 21, 2017

    @mklement0
    Michael Klement (mklement0)
    on Aug 21, 2017
    ContributorAuthor
    More actions

    isn't it more useful to pass a collection?

    It is useful in the same way that cmdlets (e.g., Get-ChildItem) return collections - but, by virtue of going through the pipeline, they output:

    • either a [System.Object[]] array
    • or, if the collection happens to contain a single item only, a scalar.

    I get the backward compatibility concern, but what good reason is there for ValueFromRemainingArguments parameters and common parameters -OutVariable, -ErrorVariable, -WarningVariable, and -InformationVariable do deviate from these fundamental behaviors?

  4. Ilya (iSazonov)
    added
    WG-Languageparser, language semanticsparser, language semantics
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on Aug 21, 2017
  5. mklement0 commented on Aug 21, 2017

    @mklement0
    Michael Klement (mklement0)
    on Aug 21, 2017
    ContributorAuthor
    More actions

    P.S.:

    Given that we're already willing to fix the issue for -OutVariable, even though it is a breaking change - #3154 - this strikes me as a great opportunity to fix the issue fundamentally, given that the other cases are probably less common than -OutVariable.

    Also, while technically a breaking change, I wouldn't expect it to be a problem in practice, given that, if a scalar is returned in the future for single-item collections, things should continue to work, thanks to being able to use .Count and indexing on scalars too.

    I suppose there could be code out there that relies on the specific collection types returned ([List[object]] for ValueFromRemainingArguments, and [System.Collections.ArrayList] for the common parameters), but that strikes me as unlikely.

  6. lzybkr commented on Aug 21, 2017

    @lzybkr
    Jason Shirk (lzybkr)
    on Aug 21, 2017
    Contributor
    More actions

    Comparing this issue to -OutVariable is interesting. I think the primary use case for OutVariable is the scalar case, which is why we were open to a change.

    ValueFromRemainingArguments has the opposite expectation - the primary case being a collection.

  7. mklement0 commented on Aug 21, 2017

    @mklement0
    Michael Klement (mklement0)
    on Aug 21, 2017
    ContributorAuthor
    More actions

    ValueFromRemainingArguments has the opposite expectation

    Absolutely - but the larger point is that all these features should be familiar PS citizens, which means:

    • use [object[]] arrays rather than some collection type not otherwise used publicly in PowerShell.

    • output a scalar if a collection happens to contain 1 item only.

  8. mklement0 commented on Aug 22, 2017

    @mklement0
    Michael Klement (mklement0)
    on Aug 22, 2017
    ContributorAuthor
    More actions

    Just ran into another case where the above expectations are confounded: #4642

  9. mklement0 commented on Nov 16, 2020

    @mklement0
    Michael Klement (mklement0)
    on Nov 16, 2020
    ContributorAuthor
    More actions

    Revisiting this I agree that it makes more sense to always create a collection for a (non-type-constrained) ValueFromRemainingArguments parameter variable, given that the point of such a parameter is to expect a variable number of arguments, and that the script/function author has to be prepared to handle multiple arguments.

    This reduces the issue at hand to suggesting use of object[] instead of List`1 - but, unlike in the #3154 case, this technical difference is unlikely to matter, so I'm closing this.

  10. Michael Klement (mklement0)
    closed this as completedon Nov 16, 2020
  11. surfingoldelephant commented on Jan 28, 2025

    @surfingoldelephant
    surfingoldelephant
    on Jan 28, 2025
    More actions

    It's worth noting there's asymmetry between positional input passed as:

    • A single scalar argument or multiple arguments vs...
    • A single collection argument

    This affects untyped or [object]/[psobject]-typed parameters.

    $a = [string[]] ('Foo', 'Bar')
    $untyped = { 
        param ([Parameter(ValueFromRemainingArguments)] $P1) 
        process { 
            [pscustomobject] @{ 
                Type  = $P1.GetType().Name
                Count = $P1.Count
                Value = $P1
                IsRef = [object]::ReferenceEquals($P1, $a)
            } 
        } 
    }
    
    & $untyped Foo
    & $untyped Foo Bar
    & $untyped $a
    
    # Type     Count Value      IsRef
    # ----     ----- -----      -----
    # List`1       1 {Foo}      False
    # List`1       2 {Foo, Bar} False
    # String[]     2 {Foo, Bar}  True
    For completeness, when the parameter is typed:
    $a = [string[]] ('Foo', 'Bar')
    $typed = { 
        param ([Parameter(ValueFromRemainingArguments)] [string[]] $P1) 
        process { 
            [pscustomobject] @{ 
                Type  = $P1.GetType().Name
                Count = $P1.Count
                Value = $P1
                IsRef = [object]::ReferenceEquals($P1, $a)
            } 
        } 
    }
    
    & $typed Foo
    & $typed Foo Bar
    & $typed $a
    
    # Type     Count Value      IsRef
    # ----     ----- -----      -----
    # String[]     1 {Foo}      False
    # String[]     2 {Foo, Bar} False
    # String[]     2 {Foo, Bar}  True

    Summary:

    1. If the parameter is untyped or [object]/[psobject]-typed:
      1. If a single scalar argument is passed, $P1 is a new List<T> instance.
      2. If multiple arguments are passed, $P1 is a new List<T> instance.
      3. If a single collection argument is passed, $P1 is a reference to that collection.
    2. If the parameter is typed as anything other than [object]/[psobject], $P1 is of whatever that type is.

    In older versions, there was no asymmetry in scenario 1. The result was invariably a new List<T> instance (for 1.3, a single-element list containing the collection).

    I believe this is a by-product of #2035.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifWG-Languageparser, language semanticsparser, language semantics

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.