PowerShell Great Debate: To Accelerate, or Not?

At his Birds of a feather session at TechEd 2013, Glenn Sizemore and I briefly debated something that I'd like to make the topic of today's Great Debate. It has to do with how you create new, custom objects. For example, one approach - which I used to favor, but now think is too long-form:

$obj = New-Object -Type PSObject
$obj | Add-Member NoteProperty Foo $bar
$obj | Add-Member NoteProperty This $that

We saw some variants in The Scripting Games, including this one:

$obj = New-Object PSObject
Add-Member -InputObject $obj -Name Foo -MemberType NoteProperty -Value $bar

I generally don't like any syntax that explicitly uses -InputObject like that; the parameter is designed to catch pipeline input, and using it explicitly strikes me as overly wordy, and doesn't really leverage the shell.

Glenn and I both felt that, these days, a hashtable was the preferred approach:

$props = @{This=$that;
           Foo=$bar;
           These=$those}

The semicolons are optional when you type the construct that way, but I tend to use them out of habits that come from other languages. The point of our debate was that Glenn would use the hashtable like this:

$obj = [pscustomobject]$props

Because he feels it's more concise, and because he puts a high value on quick readability. I personally prefer (and teach) a somewhat longer version:

$obj = New-Object -Type PSObject -Prop $props

Because, I argued, type accelerators like [pscustomobject] aren't documented or discoverable. Someone running across your script can't use the shell's help system to figure out WTF is going on; with New-Object, on the other hand, they've got a help file and examples to rely on.

(BTW, I never worry about ordered hashtables; if I need the output in a specific order, I'll use a custom view, a Format cmdlet, or Select-Object. A developer once explained to me that unordered hashtables are more memory-efficient for .NET, so I go with them).

But the big question on the table here is "to use type accelerators, or no?" You see this in many instances:

[null]Do-Something

# vs.

Do-Something | Out-Null

Same end effect of course, but I've always argued that the latter is more discoverable, while Glenn (and many others) prefer the brevity of the former.

So we'll make today's Great Debate two-pronged. What approach do you favor for creating custom objects? And, do you tend to prefer type accelerators, or no?

[boilerplate greatdebate]

About the Author

Don Jones

Don Jones is a Windows PowerShell MVP, author of several Windows PowerShell books (and other IT books), Co-founder and President/CEO of PowerShell.org, PowerShell columnist for Microsoft TechNet Magazine, PowerShell educator, and designer/author of several Windows PowerShell courses (including Microsoft's). Power to the shell!

3 Comments

  1. The different approaches in this article highlight another discussion point: performance.

    Pipelines in PowerShell are generally much slower than their non-pipelined counterpart. It is very common to see script authors prefer passing in a value to -InputObject for a command instead of using the pipeline simply because it is much more performant. I use this approach whenever I can in scripts as a best practice, whether it is for a single object or an entire collection of objects, because the scripts will run faster. This is why I'll prefer storing a custom object in a variable and then using multiple calls to Add-Member, passing that object into the -InputObject parameter instead of using a pipeline.

    Similarly, for [void] vs Out-Null, performance is once again a factor. Take a look at this:

    PS C:\> $x = 1..100000
    PS C:\> Measure-command {$x | out-null}

    Days : 0
    Hours : 0
    Minutes : 0
    Seconds : 1
    Milliseconds : 404
    Ticks : 14046281
    TotalDays : 1.62572696759259E-05
    TotalHours : 0.000390174472222222
    TotalMinutes : 0.0234104683333333
    TotalSeconds : 1.4046281
    TotalMilliseconds : 1404.6281

    PS C:\> Measure-command {[void]$x}

    Days : 0
    Hours : 0
    Minutes : 0
    Seconds : 0
    Milliseconds : 0
    Ticks : 2974
    TotalDays : 3.44212962962963E-09
    TotalHours : 8.26111111111111E-08
    TotalMinutes : 4.95666666666667E-06
    TotalSeconds : 0.0002974
    TotalMilliseconds : 0.2974

    Using the [void] accelerator is significantly faster.

    Also, while it might not appear that some accelerators are documented, they actually are. Take a look at the last entry from this command (about_Object_Creation):

    PS C:\> help pscustomobject

    Name Category Module Synopsis
    ---- -------- ------ --------
    Import-Module Cmdlet Microsoft.PowerShell.Core Adds modules to the current session.
    New-Module Cmdlet Microsoft.PowerShell.Core Creates a new dynamic module that exists only ...
    Update-Help Cmdlet Microsoft.PowerShell.Core Downloads and installs the newest help files o...
    ConvertFrom-Json Cmdlet Microsoft.PowerShell.U... Converts a JSON-formatted string to a custom o...
    Import-Csv Cmdlet Microsoft.PowerShell.U... Creates table-like custom objects from the ite...
    Measure-Object Cmdlet Microsoft.PowerShell.U... Calculates the numeric properties of objects, ...
    Registry Provider Microsoft.PowerShell.Core Provides access to the system registry keys an...
    about_Object_Creation HelpFile Explains how to create objects in Windows Powe...

    Personally, I'll never use New-Object as long as pscustomobject is available to me.

    Plus, the added benefit of being able to create objects by leveraging typecasting is both self-documenting and easier in many cases. Take this approach, for instance:

    PS C:\> [System.Reflection.Assembly]::LoadWithPartialName('System.Drawing')
    PS C:\> [System.Drawing.Point]@{x=1; y=2}

    I much prefer the typecast to System.Drawing.Point in this example, and I believe it is much easier to see what is happening here than it is to identify what the arguments are going to do in New-Object -TypeName System.Drawing.Point -ArgumentList 1,2. Now this isn't a great example, because a point object is pretty easy to guess, but leveraging hash tables for object construction is very declarative and more self documenting in general than New-Object is right now.

    If you're thinking that the syntax used above makes PowerShell look more like programming than scripting, you're right. But there are great benefits to taking the time to learn some of these tricks, and to be honest, they are pretty easy to learn.

    Kirk out.