Почему $ array -eq $ null не оценивается как $ true, если $ array содержит только один элемент $ null?

Я наконец нашел время, чтобы понять, почему Visual Studio Code требует, чтобы я поместил $null в левой части сравнения на равенство.

Обыгрывая это поведение, я обнаружил, что при сравнении массива, содержащего $null элементы, с $null с $null с правой стороны, «ожидаемое неожиданное поведение» происходит с массивами, содержащими два или более $null элементов, но не для массивов, содержащих один $null элемент. . То есть if ($array -eq $null) { 'It equals $null!' } выводит It equals $null!, если $array содержит несколько $null элементов, но не, когда $array содержит только один. Что такого в количестве $null элементов, что вызывает это несоответствие?

Следующий тестовый код демонстрирует это поведение ...

function TestForNull($description, $value)
{
    $comparisonResult = $value -eq $null;
    $ifEntered = if ($value -eq $null) {
        $true;
    } else {
        $false;
    };

    Write-Host -Object $description;
    Write-Host -Object "`t`$comparisonResult.GetType(): $($comparisonResult.GetType())";
    Write-Host -Object "`t`$comparisonResult.Length: $($comparisonResult.Length)";
    for ($i = 0; $i -lt $comparisonResult.Length; $i++)
    {
        $item = $comparisonResult.GetValue($i);
        $itemText = if ($null -eq $item) {
            '(null)';
        } else {
            $item.ToString();
        };

        Write-Host -Object "`t`$comparisonResult[$i]: $itemText";
    }
    Write-Host -Object "`t`$ifEntered: $ifEntered";
}

TestForNull '0-element array'                          @();
TestForNull '1-element array with all $nulls'          @($null);
TestForNull '2-element array with all $nulls'          @($null, $null);
TestForNull '3-element array with all $nulls'          @($null, $null, $null);
TestForNull '3-element array with one leading $null'   @($null, 2, 3);
TestForNull '3-element array with one inner $null'     @(1, $null, 3);
TestForNull '3-element array with one trailing $null'  @(1, 2, $null);
TestForNull '3-element array with two leading $nulls'  @($null, $null, 3);
TestForNull '3-element array with two boundary $nulls' @($null, 2, $null);
TestForNull '3-element array with two trailing $nulls' @(1, $null, $null);

... и выходы ...

0-element array
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 0
        $ifEntered: False
1-element array with all $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 1
        $comparisonResult[0]: (null)
        $ifEntered: False
2-element array with all $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 2
        $comparisonResult[0]: (null)
        $comparisonResult[1]: (null)
        $ifEntered: True
3-element array with all $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 3
        $comparisonResult[0]: (null)
        $comparisonResult[1]: (null)
        $comparisonResult[2]: (null)
        $ifEntered: True
3-element array with one leading $null
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 1
        $comparisonResult[0]: (null)
        $ifEntered: False
3-element array with one inner $null
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 1
        $comparisonResult[0]: (null)
        $ifEntered: False
3-element array with one trailing $null
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 1
        $comparisonResult[0]: (null)
        $ifEntered: False
3-element array with two leading $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 2
        $comparisonResult[0]: (null)
        $comparisonResult[1]: (null)
        $ifEntered: True
3-element array with two boundary $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 2
        $comparisonResult[0]: (null)
        $comparisonResult[1]: (null)
        $ifEntered: True
3-element array with two trailing $nulls
        $comparisonResult.GetType(): System.Object[]
        $comparisonResult.Length: 2
        $comparisonResult[0]: (null)
        $comparisonResult[1]: (null)
        $ifEntered: True

person Lance U. Matthews    schedule 22.12.2018    source источник


Ответы (1)


Мы уже (надеюсь) знаем, что $array -eq $null оценивает не [Boolean], а массив, содержащий элементы $array, которые равны $null. В качестве условия оператора if, что результирующий массив должен быть преобразован в [Boolean], и хотя я не смог найти никакой официальной документации PowerShell с подробным описанием того, как это происходит, именно особенности этого преобразования вызывают рассматриваемое поведение. Лучше всего это проиллюстрировать не операторами if, а просто выражениями приведения ...

PS> [Boolean] @()
False
PS> [Boolean] @($null)
False
PS> [Boolean] @($null, $null)
True
PS> [Boolean] @($null, $null, $null)
True
PS> [Boolean] @(New-Object -TypeName 'Object')
True
PS> [Boolean] @((New-Object -TypeName 'Object'), (New-Object -TypeName 'Object'))
True
PS> [Boolean] @($true)
True
PS> [Boolean] @($false)
False
PS> [Boolean] @($true, $true)
True
PS> [Boolean] @($true, $false)
True
PS> [Boolean] @($false, $true)
True
PS> [Boolean] @($false, $false)
True
PS> [Boolean] @(0)
False
PS> [Boolean] @(1)
True
PS> [Boolean] @(-1)
True
PS> [Boolean] @('')
False
PS> [Boolean] @('false')
True
PS> [Boolean] @('true')
True
PS> [Boolean] @('1')
True
PS> [Boolean] @('0')
True
PS> [Boolean] @('-1')
True

В случае, если здесь нет заметной закономерности, логика ...

  1. Пустые массивы приводятся к $false.
  2. Arrays with one element are cast to...
    • ...the length of that element as a [Boolean] if that element implements the IList interface.
    • ... результат преобразования этого элемента в [Boolean], если этот элемент не реализует IList интерфейс.
  3. Массивы с несколькими элементами приводятся к $true.

Таким образом, учитывая, что $array содержит один элемент $null (например, $array = @($null)), $array -eq $null возвращает @($null). При преобразовании @($null) в [Boolean] применяется правило 2, приведенное выше: $null преобразуется в $false, поэтому @($null) преобразуется в $false.

Учитывая, что $array содержит несколько $null элементов (например, $array = @($null, $null)), $array -eq $null возвращает массив с таким же количеством (и, что наиболее важно, множеством) $null элементов. Тот факт, что $null приводит к $false, здесь не имеет значения, потому что по правилу 3 такие массивы всегда приводят к $true.

Вот почему важно, содержит ли массив один или несколько $null элементов, поскольку возникает ловушка выполнения сравнения с $null и передачи массива в качестве первого операнда.

Дополнительная литература:

person Lance U. Matthews    schedule 22.12.2018