Окружение: операционная система «Windows 10», программа-оболочка «PowerShell» версии 7, программа-«эмулятор терминала» «Windows Terminal» версии 1.17.
В
предыдущем посте я написал на языке PowerShell функцию, которая возвращает массив класса «
System.Collections.ArrayList». Функция работает так, как я планировал. Однако, вскоре я заметил, что на выходе из функции я получил значение не класса «System.Collections.ArrayList», как ожидал, а обычный массив типа System.Object[], производного от базового класса «
System.Array». На данный момент меня такая ситуация устраивает, это изменение типа никак не мешает моим планам. Однако, я всё же хочу понимать, почему это произошло.
Для демонстрации описанного выше поведения я использую упрощенный пример функции, возвращающей массив класса «System.Collections.ArrayList»:
function getTable {
$arrL = [System.Collections.ArrayList]::new()
$i = $arrL.Add(1); $i = $arrL.Add(2); $i = $arrL.Add(3)
Write-Host $arrL.GetType()
$arrL
}
Протестируем работу этой функции:
PS C:\> $arr = getTable
System.Collections.ArrayList # тип значения до возврата
PS C:\> $arr.GetType().FullName
System.Object[] # тип значения после возврата
PS C:\> $arr
1
2
3
Обратите внимание, что внутри функции, чтобы послать сообщение в окно программы-оболочки, я использую командлет «
Write-Host». В этом месте не подойдет ни простой вывод $arrL.GetType(), ни вывод с помощью командлета «
Write-Output», так как в при использовании этих способов вывод отправляется не напрямую в окно программы-оболочки, а направляется на конвейер, то есть попадает в общий возврат функции getTable вместе с возвратом значения переменной $arrL. Командлет «Write-Host» посылает вывод напрямую в окно программы-оболочки (точнее, это окно «хоста» программы-оболочки; «хостом» программы-оболочки является программа-«эмулятор терминала», у меня это «Windows Terminal»), что мне и требуется в данном случае.
Причина описанного и как исправить
Всё, что нужно для понимания описанного, я нашел в статье «
about_Return» из документации программы-оболочки «PowerShell» на сайте компании «Microsoft» (особенно следует обратить внимание на
вторую половину статьи). Я даже немного пожалел о том, что эта статья не попалась мне в самом начале знакомства с программой-оболочкой «PowerShell» и одноимённым языком программирования.
Дело в том, что при возврате из функции любого значения-коллекции (массив любого типа - это тоже коллекция) программа-оболочка «PowerShell» разворачивает коллекцию в очередь элементов коллекции, возвращая каждый элемент коллекции по очереди. Это соответствует парадигме работы с
конвейером программы-оболочки «PowerShell».
Таким образом, из функции возвращается не одно значение-коллекция, а множество значений-элементов коллекции по очереди. Если возврат записывается в переменную, как в блоке кода выше, то возвращаемые значения-элементы собираются в обычный массив типа System.Object[], производного от базового класса «System.Array». Исходный тип коллекции теряется.
В упомянутой выше статье предлагается два способа получить из функции коллекцию исходного типа (в нашем случае - массив класса «System.Collections.ArrayList»). Я продемонстрирую тут один из этих способов, который кажется мне более читаемым в коде. Меняем код демонстрационной функции:
function getTable {
$arrL = [System.Collections.ArrayList]::new()
$i = $arrL.Add(1); $i = $arrL.Add(2); $i = $arrL.Add(3)
Write-Host $arrL.GetType()
Write-Output -NoEnumerate $arrL # измененная строка
}
Как видно в блоке кода выше, теперь для возврата значения из функции я использую командлет «
Write-Output». Этот командлет, если его использовать без дополнительных параметров, работает абсолютно так же, как и просто вывод $arrL, использованный в первом варианте этой функции. Но с помощью дополнительных параметров этого командлета мы можем настраивать возврат из функции. В данном случае используется параметр-флаг -NoEnumerate, который подавляет разворачивание коллекции в множество элементов коллекции, выполняемое по умолчанию. То есть функция должна вернуть одно значение-коллекцию исходного вида, а не множество значений-элементов коллекции.
Протестируем работу исправленного варианта функции:
PS C:\> $arr = getTable
System.Collections.ArrayList # тип значения до возврата
PS C:\> $arr.GetType().FullName
System.Collections.ArrayList # тип значения после возврата
PS C:\> $arr
1
2
3
Что и требовалось получить.