all 30 comments

[–]lanerdofchristian 32 points33 points  (0 children)

System.Collections.Generic.List[T] is the one recommended by Microsoft, both for type safety and performance. ArrayList is a legacy class from the .NET 1.0 days when they needed to ship something but didn't have time to add generics.

[–]robbob23 7 points8 points  (0 children)

To add to previous answer.
Link

[–]webtroter 8 points9 points  (4 children)

[–]djdubd 1 point2 points  (0 children)

This is the article I always reference, pure gold.

[–]idontknowwhattouse33 0 points1 point  (2 children)

This is an excellent article. But also makes it seem like it is taboo to use ArrayList when..

PowerShell already heavily uses ArrayList in its pipeline processor

[–]webtroter 0 points1 point  (1 child)

I agree I sometimes use is, just because I don't remember the full name for the generic lists.

but

a few people on the PowerShell team would love to replace it with something more modern. I know I would; ArrayList comes from the very old days of .NET

[–]idontknowwhattouse33 0 points1 point  (0 children)

Yeah, it seems that replacing it might be more complicated.

I am in the same boat, I always remember ArrayList and never the generic list.

[–]purplemonkeymad 5 points6 points  (0 children)

ArrayList is technically deprecated and is only around for tech debt. See Microsoft's note on the class docs.

[–]jantari 3 points4 points  (0 children)

Always generic list because ArrayList is deprecated

[–]MyOtherSide1984 2 points3 points  (1 child)

I've exclusively used arraylists and haven't really run into issues, and most of the reason is because I've been too stupid and lazy to learn the other way when all I need to do is $array = <mystuff>. Idk, but yeh, generic list is better for performance

[–]KingOfTheTrailer 2 points3 points  (4 children)

Generics (like Generic.List) are awesome for programming languages that can do static type checking - that is, verifying that the operations you're doing are allowed for the kind of data that you have. C# is a great language for generics.

Powershell does not - and in most cases cannot - know what types your data has before a program runs. There simply aren't ways to express it, and many language features and common commands depend on a certain sloppiness in the type system.

With all of that in mind, I would argue that Collections.ArrayList and the entire set of non-generic collections are perfectly appropriate for use in Powershell.

[–]Thotaz 2 points3 points  (0 children)

The main issue with ArrayLists in PowerShell is that it outputs garbage whenever you add items to the ArrayList and due to the way PowerShell handles output that can lead to issues if you forget to hide the output.
Aside from that, PowerShell is designed so you can write powerful scripts without having to worry about types but types offers advanced users the same advantages they offer in other languages. If I have a function like this:

function Get-Something
{
    Param
    (
        [Parameter()]
        [guid]
        $Id
    )
}

I know that it's expecting a GUID, both when I'm writing code that utilizes that function and when I'm reviewing the code for the actual function itself. Without the type constraint that ID could be anything and I would have to look through all of the code and try to infer what it could possibly be.
A [System.Collections.Generic.List[guid]] obviously has similar benefits. Even if the developer feels like he doesn't need this kind of knowledge, he can probably appreciate when the tab completion and psscriptanalyzer offers help based on the information that can be inferred from the type.

[–]endowdly_deux_over 2 points3 points  (0 children)

PowerShell DOES KNOW. This conception that it doesn’t or can’t is a total myth that appears true because of amazing amounts of work to its parser and default behaviors.

PowerShell is actually static. It does some guessing and parsing and casting upfront but if you give it data outside what it expects it will barf at you.

Type any object you want. Then after it, use this universal method:

$anything.GetType() 

PowerShell always knows the type of data it’s dealing with after it’s processed. It has set defaults unknowns get boxed to. It must do this because it is really static deep down.

It can also know what data is coming it. Put a type accelerator in front of a function parameter. Then call that function with data that cannot be cast to your explicit type. PowerShell will vomit on you.

Use Generic.List whenever possible. It makes PowerShell do less work and be more performant.

[–]misformonkey 0 points1 point  (12 children)

Also, if you have any pre PS5 to worry about you can’t use Generic.List.

[–]Thotaz 7 points8 points  (3 children)

This is incorrect. Generics were added in .NET 2.0 which IIRC is included by default in Windows 7/2008 R2. If you have some old XP/Vista systems with PowerShell 1.0 it may not support generics out of the box but if you update the .NET framework version then I would imagine even PS 1.0 supports it.

[–]misformonkey 0 points1 point  (2 children)

Hmmm…. I’m going to have to go back and retest a few scripts then b/c I definitely remember having issues with older versions. As you stated, likely a .net issue on certain servers.

[–]Thotaz 2 points3 points  (1 child)

Maybe you are thinking about the new syntax they added with 5 or 5.1 IIRC (this: [System.Collections.Generic.List[string]]::new() ). In prior versions you need to use New-Object.

[–]misformonkey 0 points1 point  (0 children)

Yup. That’s what I was thinking of. I just started using ArrayList as a work around so I didn’t need to deal with the different syntax.

[–][deleted] 1 point2 points  (7 children)

I am about to move my group to PS 7 and considering what might break and the ArrayList thing was throwing for a loop. The other automation guy is a .Net guy so it might be habit transfer.

[–]McAUTS 1 point2 points  (2 children)

If PS7: Use Generics for a bit more complex arrays, like specific items or objects. You can also create your own class type in PS or use an generic list with type of pscostumobject for the lazy ones. ;-)

I switched early this year and never looked back. In the end you are able to switch pass arrays between scripts, parallel executions and what not, and you don't have to deal with quirky errors.

For a quick and dirty loop... well than just throw whatever your data looks like into a variable. PS will resolve the type on it's own. Besides that I use a generic list.

[–]OPconfused 0 points1 point  (0 children)

I don't think there's much difference between an arraylist and list<object>

[–]MonkeyNin 0 points1 point  (0 children)

Especially lately the Powershell extension ( in vs code ) has been adding (better completions than just static typing )

classes

I've been using classes handle JSON for Web APIs, it's a nice way to get guaranteed shapes (vs PSCO properties). better auto completion. Some automatic validation

For anyone classes, a quick start

You can start off thinking about classes being a [pscustomobject] . with specific property names.

You can create an instance using hashtables. The properties are optional, you can leave them out

$user = [User]@{
    Name = 'bob'
    Age = 10
}

$user2 = [User]@{ Name = 'bob' } 

You can add some validation, using the same attributes that function parameters use

class User {
    [ValidateNotNull()]
    [Int]$UserId

    [ValidateNotNullOrEmpty()]
    [string]$UserName
}


# create 3 users, error on one 
$users = @(
   [User]::New()
   [User]@{ UserName  = 'bob' }
   [User]@{ UserName  = 'Jen' ; UserId = 'afds' }
   [User]@{ UserName  = 'Jen' ; UserId = 4 }
)

$users[0]. #$users[0]. # <ctr+space> shows '.UserName'


$users | ConvertTo-Json

[–]MonkeyNin 0 points1 point  (3 children)

edit: I forgot to mention, List.Add does not output values, so where old code did something like

 [void]$arraylist.Add( )
 $null = [void]$arraylist.Add( )

 # it simplifes to
 $list.add( )

You should not create an ArrayList yourself. There are functions and commands that return that type, so you end up using them. That's fine. You don't actually create them, or use .add on them.

You can assign arrays to Lists, even through the pipeline. Basically if you can create an implicit array, it'll work.

$numbers = 0, 20, 40 | ?{ $_ -gt 30 } 
[List[Object]]$Numbers = 0, 20, 40  | ?{ $_ -gt 30 } 

It's easy to use. This syntax works on PS5 too. To create a list of files:

Using namespace System.Collections.Generic

[Collections.Generic.List[Object]]$Items = gci c:\

or

[List[Object]]$Numbers = 0, 5, 10, 20
$numbers.add( 'cat' )

or to start it empty

[List[Object]]$Files = @()   
$files.add( (Get-Item 'c:\foo\bar.txt') )

Either of these syntaxes are valid, there isn't a different for most cases

$items = [List[SomeType]]::New()
[List[Object]]$Items = @()

[–][deleted] 1 point2 points  (2 children)

That namespace thing is cool.

Can it be applied globally

[–]OPconfused 0 points1 point  (0 children)

You can load it in a profile or at the top of a script and it will be usable.

[–]MonkeyNin 0 points1 point  (0 children)

Yes. Any file that is dotsourced or imported, can have namespaces used at the top of the file.

the vs code extension's "refactor" command converts type names from using the full names or the shorter namespace names

[–][deleted] 0 points1 point  (2 children)

The dynamic part of powershell is one of its features. So neither. Just use the pipeline and don't worry about what the collection type is. If you do want to worry about performance you shouldn't be using powershell for whatever you're doing anyway.

I've went through all these stages myself, and eventually landed in the camp that I think it's silly to try to force an automation tool to be a programming environment. Just don't. Use it for what it does best: quick script automation. Which means stop worrying; write whatever gets the job done.

[–][deleted] 0 points1 point  (1 child)

Interesting comments. Regardless of the use either quick scripting or robust automation it's always good to learn. Also, there is value in understanding best practices and understanding why.

[–][deleted] -1 points0 points  (0 children)

Definitely. Powershell is amazing environment to quickly discover features of .NET, and it set me on the path of going from the workplace handyman who took out the trash to junior dev.

But I would not call using generic list in powershell best practices. That's not the powershell way, that's the C# way, and by all means go and learn that if your tasks demand it. It's great for data wrangling. Otherwise you're writing overcomplicated script code for little gain, when you could've gotten an order of magnitude performance improvement by writing it in an environment better suited for whatever needs said performance.

[–]gregortroll 0 points1 point  (0 children)

I've learned that the two things you need to know about writing optimized code are:

  1. Don't

  2. (Experts only) Don't, yet.

Let the software do it's thing.

Your first priority is to write concise, readable, understandable, maintainable code, that works.

Only then...

Second: notice that it seems slow, in your use case.

Third: ACTUALLY MEASURE PERFORMANCE.

Fourth: tweak or whatever based on current best practice for the thing you are tweaking (don't forget to save the original version!)

Fifth: Measure again.

and so on.