I am attempting to mock up a data structure and logic to handle deferred log writing, and I am running into an odd situation. if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) {
errors when the returned arrayList is only one item, with
Cannot convert the "logged: 07/20/2019 10:56:29" value of type "System.String" to type "System.Collections.ArrayList".
Once there are two items it's fine. Now I know an array of a single item needs to be forced to be an array because PS is sometimes too clever for it's own good, but I thought the Add method of a [collections.arrayList]
produced an arrayList even on the first Add. AM I totally misunderstanding, or is my logic wrong somewhere else?
I also tried casting the results of the function call like this
if ([collections.arrayList]$deferredLog = [collections.arrayList](Get-PxDeferredLogItems)) {
but that is even worse, it errors at every call. I also tried initializing the variable to an empty arrayList with Set-PxDeferredLogItems (New-Object collections.arrayList)
in Main, rather the n initializing it on the first call of Add-PxDeferredLogItem
, but that shows the same behavior, errors until the deferred log has two items. I verified this by changing the Write-Host directly after the erroring line to Write-Host "Process deferred items $($deferredLog.count)"
, and I get errors till it shows a 2, then everything works as expected.
function Get-PxDeferredLogItems {
return $script:deferredLogItems
}
function Set-PxDeferredLogItems {
param (
[collections.arrayList]$deferredLog
)
[collections.arrayList]$script:deferredLogItems = $deferredLog
}
function Add-PxDeferredLogItem {
param (
[string]$item
)
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add($item)
}
function Add-PxLogContent {
param (
[string]$string
)
function Assert-PxWrite {
if ((Get-Random -minimum:1 -maximum:4) -gt 1) {
return $true
} else {
return $false
}
}
if ([collections.arrayList]$deferredLog = Get-PxDeferredLogItems) {
Write-Host "Process deferred items"
:deferredItemsWrite do {
if (Assert-PxWrite) {
Write-Host "$($deferredLog[0]) ($(Get-Date))"
$deferredLog.RemoveAt(0)
} else {
break :deferredItemsWrite
}
} while ($deferredLog.count -gt 0)
if ($deferredLog.count -eq 0) {
$deferredLogPending = $false
} else {
$deferredLogPending = $true
}
Set-PxDeferredLogItems $deferredLog
} else {
$deferredLogPending = $false
}
if (-not $deferredLogPending) {
if (Assert-PxWrite) {
Write-Host $string
} else {
Add-PxDeferredLogItem $string
}
} else {
Add-PxDeferredLogItem $string
}
}
### MAIN
$startTime = Get-Date
$endTime = $startTime + (New-TimeSpan -minutes:2)
Clear-Host
do {
Add-PxLogContent "logged: $(Get-Date)"
Start-SLeep -s:5
} while ((Get-Date) -lt $endTime)
if ($deferredLog = Get-PxDeferredLogItems) {
foreach ($item in $deferredLog) {
Write-Host "!$item"
}
}
EDIT: It seems to be related to passing the arrayList, as this works as expected when there are no functions involved.
Clear-Host
$deferredLogItems = New-Object collections.arrayList
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('One')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Two')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
[void]$deferredLogItems.Add('Three')
Write-Host "$($deferredLogItems.getType()) $($deferredLogItems.count) $deferredLogItems"
function Get-DeferredLogItems {
if (-not $script:deferredLogItems) {
[collections.arrayList]$script:deferredLogItems = New-Object collections.arrayList
}
[void]$script:deferredLogItems.Add("$($script:deferredLogItems.count + 1)")
return $script:deferredLogItems
}
$script:deferredLogItems = $localDeferredLogItems = $null
foreach ($i in 1..5) {
[collections.arrayList]$localDeferredLogItems = Get-DeferredLogItems
Write-Host "$($localDeferredLogItems.getType()) $($localDeferredLogItems.count) $localDeferredLogItems"
}
EDIT2: Replaced the line # reference with the actual offending line of code, because Confusion.
EDIT3: For what it's worth, forcing the results of Get-DeferredLogItems
into an array DOES work. But I still wonder why that's even needed. Why does the Add method produce a correct arrayList at first add but this gets @%#$ed up by PowerShell when passed as a return value?