4

Consider this PowerShell script:

[Hashtable] $t = @{} 
function foo($x) { $x }
$t2 = foo $t
$t3 = {param([Hashtable] $x) [Hashtable]$x}.Invoke($t)
$t4 = $function:foo.Invoke($t)
Write-Host "argument type              " $t.GetType()
Write-Host "function call              " $t2.GetType()
Write-Host "script block Invoke        " $t3.GetType()
Write-Host "function variable Invoke   " $t4.GetType()

Which outputs:

argument type              System.Collections.Hashtable
function call              System.Collections.Hashtable
script block Invoke        System.Collections.ObjectModel.Collection`1[System.Management.Automation.PSObject]
function variable Invoke   System.Collections.ObjectModel.Collection`1[System.Management.Automation.PSObject]

Why does the script block return a Collection instead of a Hashtable?
How to make the script block return a Hashtable?

PowerShell version used:

$PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.0.0
PSEdition                      Core
GitCommitId                    7.0.0
OS                             Microsoft Windows 10.0.18363
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
Konrad Jamrozik
  • 3,254
  • 5
  • 29
  • 59
  • It looks like you've already answered your own question. [MethodBase.Invoke Method](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.methodbase.invoke?view=netcore-3.1) Invokes the method or constructor reflected by this MethodInfo instance. [PowerShell.Invoke Method](https://learn.microsoft.com/en-us/dotnet/api/system.management.automation.powershell.invoke?view=pscore-6.2.0) Invoke() --- Invoke the Command synchronously and return the output PSObject collection. – postanote May 26 '20 at 05:00

2 Answers2

4

Have a look at InvokeReturnAsIs method:

[Hashtable] $t = @{} 
function foo($x) { $x }
$t2 = foo $t
$t3 = {param([Hashtable] $foo) [Hashtable]$foo}.InvokeReturnAsIs($t)
Write-Host $t.GetType()
Write-Host $t2.GetType()
Write-Host $t3.GetType()

Which outputs:

System.Collections.Hashtable
System.Collections.Hashtable
System.Collections.Hashtable

It seems to give the result you are looking for, but the documentation does not give much informations

JPBlanc
  • 70,406
  • 17
  • 130
  • 175
0

I believe the Hashtable gets converted to Collection due to a call of the ScriptBlock.Invoke method. Its return type is System.Collections.ObjectModel.Collection<System.Management.Automation.PSObject>.

Looks like this can be circumvented by invoking the script block via Invoke-Command:

[Hashtable] $t = @{} 
$t5 = Invoke-Command -ScriptBlock {param([Hashtable] $x) [Hashtable]$x} -ArgumentList $t
$t6 = Invoke-Command -ScriptBlock $function:foo -ArgumentList $t
Write-Host "Invoke-Command on script block      " $t5.GetType()
Write-Host "Invoke-Command on function variable " $t6.GetType()

This outputs:

Invoke-Command on script block        System.Collections.Hashtable
Invoke-Command on function variable   System.Collections.Hashtable
Konrad Jamrozik
  • 3,254
  • 5
  • 29
  • 59