Why the different behavior? Is it just an oversight, or is there some intentional behavior I am not understanding?
Because many years ago, someone decided that's how ArrayList should behave!
Add() returns the index at which the argument was inserted into the list, which may indeed be useful and makes sense.
With AddRange() on the other hand, it's not immediately clear why it should return anything, and if yes, what? The index of the first item in the input arguments? The last? Or should it return a variable-sized array with all the insert indices? That would be awkward! So whoever implemented ArrayList decided not to return anything at all.
In C# or VB.NET, for which ArrayList was initially designed, "polluting the pipeline" doesn't really exist as a concept, the runtime would simply omit copying the return value back to the caller if someone invokes .Add() without assigning to a variable.
The
[void]here isn't required, but I wonder if it's just best practice to have it, depending of course on the answer above. Or am I missing something there too?
No, it's completely unnecessary. AddRange() is not magically one day gonna change to output anything.
If you don't ever need to know the insert index, use a [System.Collections.Generic.List[psobject]] instead:
$list = [System.Collections.Generic.List[psobject]]::new()
# this won't return anything, no need for `[void]`
$list.Add(123)
If for some reason you must use an ArrayList, you can "silence" it by overriding the Add() method:
function New-SilentArrayList {
# Create a new ArrayList
$newList = [System.Collections.ArrayList]::new()
# Create a new `Add()` method, then return the list
$newAdd = @{
InputObject = $newList
MemberType = 'ScriptMethod'
Name = 'Add'
Value = {param(
this.AddRange(@($obj))}
}
Write-Output $(
Add-Member @newAdd -Force -PassThru
) -NoEnumerate
}
Now your ArrayList's Add() will never make a peep again!
PS C:\> $list = New-SilentArrayList
PS C:\> $list.Add(123)
PS C:\> $list
123
Answer from Mathias R. Jessen on Stack OverflowWhy the different behavior? Is it just an oversight, or is there some intentional behavior I am not understanding?
Because many years ago, someone decided that's how ArrayList should behave!
Add() returns the index at which the argument was inserted into the list, which may indeed be useful and makes sense.
With AddRange() on the other hand, it's not immediately clear why it should return anything, and if yes, what? The index of the first item in the input arguments? The last? Or should it return a variable-sized array with all the insert indices? That would be awkward! So whoever implemented ArrayList decided not to return anything at all.
In C# or VB.NET, for which ArrayList was initially designed, "polluting the pipeline" doesn't really exist as a concept, the runtime would simply omit copying the return value back to the caller if someone invokes .Add() without assigning to a variable.
The
[void]here isn't required, but I wonder if it's just best practice to have it, depending of course on the answer above. Or am I missing something there too?
No, it's completely unnecessary. AddRange() is not magically one day gonna change to output anything.
If you don't ever need to know the insert index, use a [System.Collections.Generic.List[psobject]] instead:
$list = [System.Collections.Generic.List[psobject]]::new()
# this won't return anything, no need for `[void]`
$list.Add(123)
If for some reason you must use an ArrayList, you can "silence" it by overriding the Add() method:
function New-SilentArrayList {
# Create a new ArrayList
$newList = [System.Collections.ArrayList]::new()
# Create a new `Add()` method, then return the list
$newAdd = @{
InputObject = $newList
MemberType = 'ScriptMethod'
Name = 'Add'
Value = {param(
this.AddRange(@($obj))}
}
Write-Output $(
Add-Member @newAdd -Force -PassThru
) -NoEnumerate
}
Now your ArrayList's Add() will never make a peep again!
PS C:\> $list = New-SilentArrayList
PS C:\> $list.Add(123)
PS C:\> $list
123
Apparently I didn't quiet understand where you where heading to.
"Add pollutes the pipeline", at a second thought is a correct statement but .Net methods like $variable.Add('String') do not use the PowerShell pipeline by itself (until the moment you output the array using the Write-Output command which is the default command if you do not assign it to a variable).
The
Write-Outputcmdlet is typically used in scripts to display strings and other objects on the console. However, because the default behavior is to display the objects at the end of a pipeline, it is generally not necessary to use the cmdlet.
The point is that Add method of ArrayList returns a [Int32] "The ArrayList index at which the value has been added" and the AddRange doesn't return anything. Meaning if you don't assign the results to something else (which includes $Null = $test.Add('Single')) it will indeed be output to the PowerShell Pipeline.
Instead you might also consider to use the Add method of the List class which also doesn't return anything, see also: ArrayList vs List<> in C#.
But in general, I recommend to use native PowerShell commands that do use the Pipeline
(I can't give you a good example as it is not clear what output you expect but I noticed another question you removed and from that question, I presume that this Why should I avoid using the increase assignment operator (+=) to create a collection answer might help you further)
Videos
I'm trying to call a function that will return a list of AD users. Here's what I have so far:
using namespace System.Collections.Generic;
function foo {
$list = [List[object]]::new()
$list.Add($(Get-ADUser -filter 'name -eq "bob"'))
, $list
}
$userlist = [List[object]]::new()
$userlist.Add($(foo))Edit: fixed the code I had. It wasn't correct, although I am still getting the error
Cannot find an overload for "Add" and the argument count: "1".
Edit 2: Changed .Add to .AddRange and added conversion per u/y_Sensei. I may or may not need more caffeine, so hopefully I didn't botch anything
using namespace System.Collections.Generic;
function foo {
$list = [List[hashtable]]::new()
$list.AddRange([List[hashtable]]@($(Get-ADUser -filter 'name -eq "bob"')))
, $list
}
$userlist = [List[hashtable]]::new()
$userlist.AddRange([List[hashtable]]@($(foo)))
I'm trying to add values to an arraylist where the values are held in variables.
$foo1="red"
$foo2="green"
$colors = [System.Collections.ArrayList]::new()
$colors.Add({$foo1,$Foo2})
$colorsBut this outputs "$foo1,$Foo2" when I would expect "red,green".
What's the correct way to achieve what I want?
Context: I am using runspaces to speed up larger permission reports. This is the last addition to the array/list so whether it becomes fixed or not is not important. I am just worried about speed mostly and efficiency next.
Data can be 5 objects total up to 9 million objects (because who deletes old data?)
.Add Method:
ForEach ($t in $Threads) {
Foreach ($Perm in ($t.Runspace.EndInvoke($t.Invoker))) {
$ThreadResults.Add($Perm)
}
$t.Runspace.Dispose()
}+= Method:
$ThreadResults += ForEach ($t in $Threads) {
$t.Runspace.EndInvoke($t.Invoker)
$t.Runspace.Dispose()
}