Why doesn't the .NET Framework ArrayList class .Add method work in a PowerShell implementation?
I was seeking to return a list of dates from a function as an array with a user-defined date range as parameters. The array of dates would then be referenced to move and read files that are named with date stamps. Creating a dynamic array I was incorrectly calling a .NET .Add method on an @() array declaration.
Exception calling "Add" with "1" argument(s): "Collection was of a fixed size."
I thought I needed to find a dynamic array type. I discovered that objects should be added to PowerShell arrays using the += syntax. I found the .NET ArrayList class. Now I had a dynamic array object. Documentation said I should use the .Add method to add elements to the collection. It produced a date range but I observed weird dates returned, such as:
Monday, January 1, 0001 12:00:00 AM
To add an element to a .NET ArrayList in Powershell use the .Add method. It's documented. Why doesn't this work? I could obtain accurate results by using the += method for adding objects to an ArrayList. It will produce the errors I described:
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)
$datesArray = [System.Collections.ArrayList]@() # Second method
for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
$datesArray.Add($d)
}
}
Return $datesArray
}
# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range
$datesList = Get-DateRangeList $startDate $endDate
# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list
# of date-stamped files to retrieve
$d
}
Most of the time I see array addition, it's totally unnecessary. The Powershell pipeline will automatically create arrays for you any time an expression returns more than one object, and it will do it very efficiently.
Consider:
Clear-Host
Function Get-DateRangeList {
[cmdletbinding()]
Param (
[datetime] $startDate,
[datetime] $endDate
)
$datesArray =
for ($d = $startDate; $d -le $endDate; $d = $d.AddDays(1)) {
if ($d.DayOfWeek -ne 'Sunday') {
$d
}
}
Return ,$datesArray
}
# Get one week of dates, ending with yesterday's date
$startDate = Get-Date
$endDate = $startDate.AddDays(-1) # Get yesterday's date as last date in range
$startDate = $endDate.AddDays(-7) # Get 7th prior date as first date in range
$datesList = Get-DateRangeList $startDate $endDate
# Loop through the dates
Foreach ($d in $datesList) {
# Do something with each date, e.g., format the date as part of a list of date-stamped files to retrieve
“FileName_{0}.txt" -f $d.ToString("yyyyMMdd")
}
All that's required is to create and output your objects, and assign the result back to your variable and you'll have an array.
Regarding 3rd paragraph of OP: Collections.arraylist does work in powershell, for instance:
# Create arraylist with space for 20 object
$ar = new-object collections.arraylist 20
$ar.add("hello everybody")
$ar.add([datetime]::now)
$ar.add( (gps)[9])
$ar[0] # returns string
$ar[1] # returns datetime
$ar[2] # returns tenth process
$ar.count # returns 3
I think the takeaway from this is to read the MSDN documentation for arraylist more carefully.
If you use += on an arraylist in PS, it takes the elements from the arraylist, and the new element and creates an array. I believe that is an attempt to shield users from the complexity of .NET that you stumbled upon. (I suspect that one of the PS product team's primary use case is a user who is not familiar with .NET in general and arraylist in particular. You apparently don't fall in that category.)
I will mention a stumbling block with PS and arrays. PS will automatically unroll arrays in some cases. For examples, if I have an array of chars and I want to create a string (using the String..ctor([char[]]) overload) then this doesn't work:
# Fails because PS unrolls the array and thinks that each element is a
# different argument to String..ctor
$stringFromCharArray = new-object string $charArray
# Wrap $charArray to get it to work
$stringFromCharArray = new-object string @(,$charArray)
# This also works
$stringFromCharArray = new-object string (,$charArray)
There are also similar issues when you pass an array down a pipeline. If you want the array passed down the pipeline (versus the array elements) then you need to wrap it in another array first.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With