Почему операторы сравнения PowerShell не перечисляют коллекции размера 1? - PullRequest
0 голосов
/ 01 ноября 2018

При проверке переменных и наборов переменных на нулевое значение, операторы сравнения, кажется, перечисляют коллекции размером 2 или более:

> if ( @( $null, $null ) -eq $null ) { $True } else { $False }
True

Но они не для коллекций размера 1:

> if ( @( $null ) -eq $null ) { $True } else { $False }
False

Я знаю, что лучше всего сравнивать нуль с помощью левой части ($null -eq @( $null )), но может кто-нибудь объяснить, что здесь происходит? Я подозреваю, что происходит нечто более тонкое, что влияет на другой код, который я пишу.

Почему эти два результата отличаются?

Ответы [ 2 ]

0 голосов
/ 01 ноября 2018

ТЛ; др

В условных / неявных логических контекстах PowerShell:

  • Одиночные -элементные массивы обрабатываются как скаляры : то есть их один-единственный элемент сам интерпретируется как логическое значение. [1]

  • 2 + -элементы массива всегда $true независимо от их содержания.


С массивом в качестве LHS , операторы с поддержкой массива, такие как -eq, неизменно также , вывод , массив .

Поскольку все элементы массива $null и вы сравниваете с $null, ваше сравнение будет эффективным no-op - например, @( $null ) -eq $null приведет к @( $null ) - и ваши условия эквивалентно:

[bool] @( $null, $null ) # -> $true - array with 2+ elements is always $True
[bool] @( $null )        # -> $false(!) - treated like: [bool] $null

Возможно, что удивительно, неявная логическая логика применяет конвейерную логику к массиву :

То есть массив с одним -элементом (концептуально) развернут , а его элемент интерпретируется как логическое значение.

Следовательно, [bool] @( $null ) обрабатывается так же, как [bool] $null, то есть $false.

Обычно @( <one-and-only-element> ) (или , <one-and-only-element>) обрабатывается так же, как <one-and-only-element> в логическом контексте.

Напротив, , если массив имеет 2 или более элементов , это всегда $true в логическом контексте , даже если все его элементы будут индивидуально считаться $false.


Обходной путь для проверки, является ли произвольный массив пустым :

В качестве условия укажите .Count свойство:

if ( (<array>).Count ) { $true } else { $false }

Вы можете добавить -gt 0, но это не является строго необходимым, потому что любое ненулевое значение неявно $true.

Применительно к вашему примеру:

PS> if ( ( @($null) -eq $null ).Count ) { $true } else { $false }
True

Проверка произвольного значения для (скалярного) $null:

if ($null -eq <value>) { $true } else { $false }

Обратите внимание, как $null необходимо использовать в качестве LHS , чтобы не дать логике фильтрации массива вступить в силу, если <value> будет массивом.

Это также причина, по которой код Visual Studio с расширением PowerShell рекомендует «$ null должен быть слева от сравнений», если вы пишете что-то вроде $var -eq $null.


[1] Сводка по булевому преобразованию :

  • Среди скаляров :

    • Ниже приведены неявно $false:

      • '' / "" (пустая строка)

      • 0 (любого числового типа).

      • $null

        • Подводный камень : Сравнение $null с логическим значением явно с -eq равно всегда $false, даже с $null как RHS (где RHS обычно приводится к типу LHS:

           $false -eq $null # !! $false - unlike `$false -eq [bool] $null`
          
    • Подводный камень : Любая непустая строка оценивается как $true

      • например, [bool] 'False' is $true

      • Обратите внимание, что это отличается от явного разбора строки : [bool]::Parse('false') возвращает $false$true для 'true', но больше ничего не распознает).

    • Экземпляры любого другого (не относящегося к коллекции) типа неявно $true, включая типы [pscustomobject] и [hashtable] (которые PowerShell обрабатывает как отдельный объект, а не как коллекция записей).

  • Среди коллекций , таких как массивы (точнее, типы, подобные коллекциям, которые реализуют интерфейс IList - см. исходный код ):

    • Пусто Коллекции всегда $false, так же как и специальное значение "нулевой коллекции", указывающее на отсутствие вывода команды [System.Management.Automation.Internal.AutomationNull]::Value.

    • Подводные камни : Одноэлементные коллекции оценивать по:

      • Если единственным элементом является скаляр : его логическое значение
      • Если этот элемент сам по себе является collection : $true, если он содержит хотя бы 1 элемент (независимо от того, что это за элемент).
    • 2 + -элементные коллекции всегда $true.

0 голосов
/ 01 ноября 2018

Следующие позиции оцениваются как $false:

@()
0
$null
$false
''

В вашем первом примере:

@($null, $null) -eq $null

Это оценивается как $null, $null, который является ненулевой коллекцией, поэтому это $true. Вы можете наблюдать это со следующим:

[bool]($null, $null)

Во втором примере вы наблюдаете фильтрацию массива, как в первом случае, но возвращает скаляр (вместо массива), поскольку только один элемент массива соответствует фильтру:

@($null) -eq $null

Это оценивается как @($null), но PowerShell оценивает его как скаляр в логическом контексте, поэтому возвращает $false, наблюдаемое:

[bool]@($null)

Сноска: в powershell v2 произошла ошибка с фильтрацией $null, которая привела к левому сравнению $null. Эта ошибка привела к тому, что if/else блоки были полностью пропущены.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...