PowerShell: возврат массива из функции

Feb 27, 2023 12:56

Окружение: операционная система «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

Что и требовалось получить.

Инструмент, Образование, Программирование

Previous post Next post
Up