Previously in in April, I mentioned something C# developers should pay attention to when they start writing PowerShell code. Now, I want share another common PowerShell behavior that will be confusing at first for a C# developer and how to handle it.
Let's start with this code:
$a = New-Object 'System.Collections.Generic.List[string]'
$a.Add("A")
$a.Add("B")
$a.Add("C")
$a.GetType().Name
What do you think will be printed by the last line? If you said "List<string>" you are correct – however, it is displayed in a very ugly way:
List`1
Let's try another case:
$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string])
$col2 = $table.Columns.Add( "Col2", [int])
$table.Rows.Add( "bar", 0 )
$table.Rows.Add( "beer", 1 )
$table.Rows.Add( "baz", 2 )
$table.GetType().Name
As expected the last line prints:
DataTable
Using Functions
Let's put that same code into functions:
function f1()
{
$arr = New-Object 'System.Collections.Generic.List[string]'
$arr.Add("A")
$arr.Add("B")
$arr.Add("C")
return $arr
}
function f2()
{
$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string]) | Out-Null
$col2 = $table.Columns.Add( "Col2", [int]) | Out-Null
$table.Rows.Add( "bar", 0 ) | Out-Null
$table.Rows.Add( "beer", 1 ) | Out-Null
$table.Rows.Add( "baz", 2 ) | Out-Null
return $table
}
Let's call those functions and check the return type
$a = f1
$a.GetType().Name
$t = f2
$t.GetType().Name
This piece of code will print out two things. Based on our previous experience we will expect to see:
List`1
DataTable
Here's what we actually see:
Object[]
Object[]
If you come from a C# background this is very surprising. All I can say is that PowerShell doesn't always follow the semantics and behavior we are used to.
In this case, when a PowerShell function detects that you are retuning a enumerable object (such as a collection), it places each member of the collection on the pipeline as a separate item which in turn get collected into an Object[]
The Solution
This is going to look ugly, but the way to get around this is to return your collections as a single element array.
function f1()
{
$arr = New-Object 'System.Collections.Generic.List[string]'
$arr.Add("A")
$arr.Add("B")
$arr.Add("C")
return @(,$arr)
}
function f2()
{
$table = New-Object system.Data.DataTable
$col1 = $table.Columns.Add( "Col1", [string]) | Out-Null
$col2 = $table.Columns.Add( "Col2", [int]) | Out-Null
$table.Rows.Add( "bar", 0 ) | Out-Null
$table.Rows.Add( "beer", 1 ) | Out-Null
$table.Rows.Add( "baz", 2 ) | Out-Null
return @(,$table)
}
And now we will get what we expect:
List`1
DataTable