Проверьте, пуст ли IEnumerable в PowerShell - PullRequest
0 голосов
/ 24 мая 2018

Есть ли собственный способ проверки PowerShell, если IEnumerable пусто?

Я знаю, что могу позвонить Linq.Enumerable.Any так:

[Linq.Enumerable]::Any($enumeration)

Но я надеюсь, что есть более родной путь.

1 Ответ

0 голосов
/ 27 мая 2018

К сожалению, есть нет Собственный способ PowerShell, начиная с Windows PowerShell v5.1 / PowerShell Core v6.1 , и хотя [Linq.Enumerable]::Any() является как минимум кратким, он может сломать в различных сценариях:

# These invocations all FAIL with
# 'Cannot find an overload for "Any" and the argument count: "1"'
foreach ($enumerable in 1.5, $null, (& {}), (1,2,3)) {[Linq.Enumerable]::Any($enumerable)}
  • Теперь, 1.5, $null и & {} (что соответствует"нулевая коллекция") делать не реализовывать [IEnumerable] ([System.Collections.IEnumerable] или универсальный аналог), но в мире PowerShell все перечислимо, даже скаляры а в конвейере - но не с foreach (!) - даже $null.Заметным исключением является «нулевая коллекция» ([System.Management.Automation.Internal.AutomationNull]::Value), единственная цель которой - сигнализировать, что перечислять ничего нельзя (и в качестве такового можно утверждать, что в качестве особого случая перечисления это следует реализовать [IEnumerable]).

  • Однако 1, 2, 3 делает реализацию [IEnumerable]: это обычный массив PowerShell типа [object[]];в то время как вы можете исправить этот конкретный случай с любопытным явным приведением [object[]], оно, очевидно, не является общим решением, так как потенциальное преобразование в массив вызывает полное перечисление - дополнительную информацию см. в нижнем разделе.

Надежное, но громоздкое решение PowerShell, предназначенное только для собственных функций, которое обрабатывает все вышеперечисленные случаи, будет (PSv4 +):

# Returns $True if $enumerable results in enumeration of at least 1 element.
# Note that $null is NOT considered enumerable in this case, unlike 
# when you use `$null | ...`
$haveAny = try { $enumerable.ForEach({ Throw }); $False } catch { $True }

Вышеэто:

  • неуклюжий и трудно запоминающийся.
  • требует, чтобы входные данные уже были сохранены в переменной ($enumerable в приведенном выше примере).

Однако в контексте отложенной оценки PowerShell обычно происходит от прямых вызовов командлетов , отправляющих свой выходной объект по объекту через конвейер (другими словами, это PowerShell конвейер , который выполняет отложенную оценку), и этот аспект потерян при захвате вывода в переменную , потому что ПауэЗатем rshell статически собирает выходные данные в массив ([object[]]).

Следовательно, собственное решение PowerShell будет гипотетический Test-Any командлет , который:

  • получает входные данные через конвейер , объект за объектом.
  • выходы $True если получен хотя бы один входной объект.

Начиная с Windows PowerShell v5.1 / PowerShell Core v6.1, однако, остановить конвейер по требованию * невозможно1106 *, что необходимо сделать командлету Test-Any для эффективной работы (во избежание полного перечисления):

  • Можно найти давний запрос функции на GitHub .

  • Реализация эффективная Test-Any, которая работает вокруг ограничения путем использования частного PowerShell типа банкаможно найти в этом ответе моего, любезно предоставлено PetSerAl ;помимо использования частного типа, вы также понесли штраф за производительность компиляции по требованию при первом использовании в сеансе.


Необязательночтение: использование [Linq.Enumerable]::Any($enumerable) с массивами PowerShell

1, 2, 3 (часто обозначаемое как @(1, 2, 3), но это не обязательно) является экземпляром массива [object[]], тип массива PowerShell по умолчанию,

Мне неясно, почему вы не можете передать такие массивы в .Any() как есть , учитывая, что он работает с явным приведением к того же типа : [Linq.Enumerable]::Any([object[]] (1,2,3)) # OK

Сбор команды для мультиобъектного вывода также (неявно) создает массив [object[]], который, однако, может быть передан как-is: [Linq.Enumerable]::Any((Get-ChildItem)) # OK

Наконец, массив, созданный с специфическим типом , может быть передан как есть:
$intArr = [int[]] (1, 2, 3); [Linq.Enumerable]::Any($intArr) # OK

Если кто-нибудь знаетпочему вы не можете использовать непосредственно построенный массив [object[]] в этом контексте и если для этого есть веские причины, сообщите нам.

...