More New Stuff in PowerShell V5: A NEW Way to Construct Things

Usually we can build out a new instance of a .Net object we have to use New-Object and pass the required parameters to accomplish this. Or at least that is the most common approach to the subject. We can also use [activator] type as well to build out the same instance with a little bit better performance. Both of these are compatible since at least V2 (I don’t deal with V1, so someone who still has this running can verify in the comments). And even sometimes you can use a hash table with a .Net type to build out the instance as well (works well with WPF and WinForms).

Refresher

We need to find a .Net type that will make for a good demo. For this I will go with Net.Sockets.TCPClient as it allows some extra arguments to supply if needed.

I now need to see what kind of parameters are available with this type. I can find this easily by using my Get-Constructor function that I wrote back here.

 
Get-Constructor Net.Sockets.TcpClient

image

I can see 4 possible constructor possibilities here. For the sake of examples, I will use the last one requiring a hostname and a port to check against.

On a side note, if you wanted to see how many .Net types have constructors, you can give this a run:

[appdomain]::CurrentDomain.GetAssemblies() | ForEach {$_.GetExportedTypes() | Where {
    $_.GetConstructors()
}} | Select -Expand FullName | Sort | Out-Host -Paging 

image

New-Object Approach

This is the most common approach that 99% of everyone in PowerShell uses when they create a .Net instance (or COM object, but I am not focused on COM today). Using New-Object is simple to use, just run the cmdlet, pass the .Net type and any arguments, if required.

 New-Object Net.Sockets.TcpClient –ArgumentList $Env:Computername, 135

 

image

[activator] Approach

This is a lesser known (OK, probably more like almost unknown) approach that you can use to create both a  .Net instance or COM object. This is done by using the [Activator] type accelerator and using the CreateInstance() method.

There are a lot of possible method parameters here, but the one I am focused on is the use of the Type and System.Object[] parameters to add the required values in the creation of the object.

SNAGHTML1f05393f

[Activator]::CreateInstance([Net.Sockets.TcpClient], @($Env:Computername,135))

image

Same as New-Object, I connect to the system and validate that port 135 is open.

What’s NEW

Now for the new approach to performing the same thing as what we have done with New-Object and [Activator]::CreateInstance(). In PowerShell V5 (currently in September Preview at the time of this post), all .Net types have an added New() operator which allows you to construct .Net instances more easily. More than just that, you also now have a way to see the possible constructor parameter options as well.

image

Just like with my function, we can see the actual parameter name as well as the type that the parameter is expecting to be passed into it. So, in this case, I will use the same approach as before and supply the required parameters to do a port check.

[Net.Sockets.TcpClient]::New($Env:Computername, 135)

 

image

Perfect! As you can see, I get the exact same output as the previous approaches. So can this be used for anything that has an available constructor? Well, for the most part, you can. I have found at least one instance where there appears to be some sort of issue. For some reason, DateTime throws a casting error when using PSCreateInstanceBinder. I’m not sure why this is happening, but asked the question on the MVP mailing list and will provide an answer when I hear back from someone on whether this is a bug or something else. (08 SEPT 2014): This was confirmed as a bug by the PowerShell team.

image

Performance Testing

The last thing I wanted to see with this is which one is the fastest to build out the .Net instance. I wanted to use DateTime as the type, but since that one has some flawed issues with the New() method, I am going with Net.Sockets.TcpClient and supplying no parameters to make sure the test is as clean as possible in just creating an object that is waiting for some action. So with that I ran a quick check…

{$Time = (1..1E5 | ForEach {
 (Measure-Command {
 New-Object Net.Sockets.TcpClient
 }).TotalMilliseconds
} | Measure-Object -Average).Average
[pscustomobject]@{
 Name = “New-Object”
 Type = 'Net.Sockets.TCPClient'
 Cycles = 100000
 MillisecondsSeconds = [math]::Round($Time,4)
}

$Time = (1..1E5 | ForEach {
 (Measure-Command {
 [Activator]::CreateInstance([Net.Sockets.TcpClient])
 }).TotalMilliseconds
} | Measure-Object -Average).Average
[pscustomobject]@{
 Name = “[Activator]::CreateInstance()”
 Type = 'Net.Sockets.TCPClient'
 Cycles = 100000
 MillisecondsSeconds = [math]::Round($Time,4)
} 

$Time = (1..1E5 | ForEach {
 (Measure-Command {
 [Net.Sockets.TcpClient]::New()
 }).TotalMilliseconds
} | Measure-Object -Average).Average
[pscustomobject]@{
 Name = “[<Type>]::New()”
 Type = 'Net.Sockets.TCPClient'
 Cycles = 100000
 MillisecondsSeconds = [math]::Round($Time,4)
}}.InvokeReturnAsIs() | Format-Table -AutoSize

… and got the following results:

image

The Activator approach has an incredibly small performance difference than with using the New() operator. In fact, those two could go either way (and in fact they did on multiple tests). But the bigger thing to notice is that New-Object is slower than both of them. In the big scheme of things, it really isn’t that big of a performance impact, but if you are looking to squeeze as much performance as you can from your scripts, then either New() or [activator]::CreateInstance() would be the way to go. Note that [activator] is good with PowerShell V2 for backwards compatibility on your scripts.

This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

2 Responses to More New Stuff in PowerShell V5: A NEW Way to Construct Things

  1. rkeithhill says:

    FYI, the reason System.Activator.CreateInstance exists is that compiled languages like C# and VB.NET need a way to create objects based on a type name determined at runtime i.e. a string object containing the type name. For example, Activator.CreateInstance(“System”, containsNameOfTypeToCreate);. The C#/VB.NET version of “new” requires a type name identifier for the type to be created at compile time e.g. new TCPClient();. Regarding [DateTime]::new(), apparently that is a bug in the Sept CTP drop.

    • Boe Prox says:

      Thanks, Keith! I saw Jason’s response on the MVP list regarding this being a bug with DateTime. This was actually a bug in the July Experimental release as well and I decided to verify in Sept before posting.
      That is some excellent information on System.Activator.CreateInstance. Definitely stuff I didn’t know before.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s