question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Surprising behavior of @() (array subexpression operator) with arrays/collections created with New-Object

See original GitHub issue

tl; dr:

This issue is written based on the following, currently unfulfilled expectation:

  • When you wrap a New-Object call that outputs an array / collection in @(), it should not create an array wrapper around it.

@PetSerAl disagrees with my expectation (quotes from the comments on this SO post that inspired this issue, part of which is reprinted below):

What is unexpected about this behavior? New-Object writes a single element to the pipeline and @() wraps it in an array.

On the tangentially related issue that @() preserves the specific array type:

Also, IMHO, @([int[]] (1, 2)).GetType().Name [returning Int32[]] is a bug (over-optimization; it returns Object[] in v2)


As of Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.4, @() unexpectedly wraps arrays / collections instantiated directly as .NET types with the New-Object cmdlet in an outer, single-element array; in other words: it doesn’t recognize that the results already are array-valued:

> @(New-Object 'Object[]' 2).Count; @(New-Object 'Object[]' 2)[0].Count
1  # !! The array was unexpectedly wrapped in an outer single-item array.
2  # !! Element [0] contains the original array.

> @(New-Object 'System.Collections.ArrayList').Count; @(New-Object 'System.Collections.ArrayList')[0].Count
1  # !! The array list was unexpectedly wrapped in an outer single-item array.
0  # !! Element [0] contains the original (empty) array list.

To contrast the surprising New-Object behavior above with commands that should be equivalent, but work as expected:

> @((New-Object 'Object[]' 2)).Count
2 # OK - !! Simply enclosing the New-Object call in (...) made the difference.

> @([int[]] (1, 2)).Count
2 # OK - using a cast in lieu of New-Object

> @([System.Collections.ArrayList]::new()).Count
0 # OK - using the static ::new() method in lieu of New-Object

Environment data

PowerShell Core v6.0.0-beta.4 on macOS 10.12.5
PowerShell Core v6.0.0-beta.4 on Ubuntu 16.04.2 LTS
PowerShell Core v6.0.0-beta.4 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.413 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:16 (16 by maintainers)

github_iconTop GitHub Comments

1reaction
PetSerAlcommented, Jul 18, 2017

@daxian-dbw But where I can see that definition of array expression? Before now I always expect it to return new array object each time. I myself use $b = @($a) (without cast although) as array copy operator, and I really do not like if it stop copying array at some point in the future.

1reaction
daxian-dbwcommented, Jul 17, 2017

Great discussions!!!

@PetSerAl is right, PowerShell unravels collection results from expressions, but not commands. Commands themselves decide whether the result writen to pipe should be unravelled or not (Cmdlet.WriteObject(object) and Cmdlet.WriteObject(object, enumerateCollection) for C# and Write-Output and Write-Output -NoEnumerate for script).

As for the quote, here is more context on it:

Most programming languages have some kind of array literal notation similar to the PowerShell hash literal notation (@{...}), where there’s a beginning character sequence followed by a list of values, followed by a closing character sequence. Here’s how array literals are defined in PowerShell: They’re not. There’s no array literal notation in PowerShell. […] instead of having array literals, there’s a set of operations that create collections as needed.

I think the point is that you don’t need a notation (beginning char sequence + closing char sequence) to create an array in PowerShell, for example, 1,2,3,4 is defining an array. But PowerShell does have ArrayLiteralRule in parser and ArrayLiteralAst.

The situation changes with a cast

The ArrayLiteralAst is embedded in the ConvertExpressionAst:

{ [int[]] (1, 2, 3) }.Ast.EndBlock.Statements[0].PipelineElements[0].Expression.Child.Pipeline.PipelineElements[0
].Expression.GetType().Name
ArrayLiteralAst
Read more comments on GitHub >

github_iconTop Results From Across the Web

Objects with no '.Count' Property - use of @() (array ...
In PetSerAl's other examples, @() 's behavior with New-Object -created arrays and collections - may be surprising - see below.
Read more >
Chapter 4. Advanced operators and variables
This chapter covers. Operators for working with types; Unary operators; Grouping and subexpressions; Array, property, and method operators; The format and ...
Read more >
Effective PowerShell Item 8: Output Cardinality – Scalars ...
Since $files was already set to $null, the array subexpression operator just creates an array with a single element – $null – which...
Read more >
Mastering PowerShell Arrays: Unleash Your Potential
Master the art of arrays in PowerShell: create dynamic collections, manipulate data, compare arrays, handle errors, and unleash the full ...
Read more >
The Apache Groovy programming language - Semantics
Expressions are the building blocks of Groovy programs that are used to reference existing values and execute code to create new ones. Groovy...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found