Как правильно использовать оператор ForEach() списка?

avatar
Gregory Suvalian
17 декабря 2018 в 22:12
3409
3
2

Я не понимаю, что я делаю неправильно в синтаксисе метода ForEach списка?

PS D:\ntt> $nicInfo.IpConfigurations.Count
2
PS D:\ntt> $nicInfo.IpConfigurations[0]

PrivateIpAddressVersion Name      Primary PrivateIpAddress PrivateIpAllocationMethod Subnet Name PublicIpAddress Name ProvisioningState
----------------------- ----      ------- ---------------- ------------------------- ----------- -------------------- -----------------
IPv4                    ipconfig1 True    10.233.0.4       Dynamic                                                    Succeeded


PS D:\ntt> $nicInfo.IpConfigurations.GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     List`1                                   System.Object


PS D:\ntt> $nicInfo.IpConfigurations.ForEach({$_})
PS D:\ntt>
Источник
Olaf
17 декабря 2018 в 22:18
1

Как вы создали объект? Вы уверены, что этот метод существует? Как насчет использования стандартного цикла foreach?

Gregory Suvalian
17 декабря 2018 в 22:20
0

Этот объект создается путем возврата другого командлета. Трубопровод к Foreach-Object работает нормально

Lee_Dailey
17 декабря 2018 в 22:25
1

это похоже на баг! [ухмылка] метод .Where работает. ни $_, ни $PSItem не работают в .ForEach(), когда типом коллекции является Generic.List. как интересно... использование Write-Host отправляет вывод на хост, но не $_. использование Write-Output вообще ничего не показывает. это похоже на сбой в обработке выходного потока.

Gregory Suvalian
17 декабря 2018 в 22:29
0

Это работает, хотя ...GetEnumerator().Foreach()

Lee
17 декабря 2018 в 22:30
1

Чего вы ожидаете? ForEach выполняется из-за побочных эффектов, а у вас, похоже, их нет. Что делает $nicInfo.IpConfigurations.ForEach({param($i) write-host "$i"})?

Gregory Suvalian
17 декабря 2018 в 22:31
1

Я хочу выполнить некоторые манипуляции с данными в операторе foreach, пример просто и пример.

user4003407
18 декабря 2018 в 03:48
3

У @GregorySuvalian List<T> есть собственный метод ForEach. Из-за этого он предпочтительнее, чем ForEach PowerShell добавляет в коллекции.

Ответы (3)

avatar
mklement0
19 декабря 2018 в 04:49
2

The Проблема в том, что PowerShell собственного .ForEach() метод сбора есть Preempted по тип List<T> собственного .ForEach() метод в данном случае:

  • Собственный PowerShell .ForEach({ ... }):

    • определяет $_ как доступный входной объект для аргумента блока сценария ({ ... })
    • пропускает любые выходные данные, созданные внутри блока сценария, через (в поток успешных выходных данных PowerShell).
  • Напротив, List<T> .ForEach({ ... }) преобразует блок скрипта в делегат Action<T>, что имеет следующие последствия:

    • Делегат не знает о $_ внутри блока скрипта и вместо этого получает единственный аргумент, к которому нужно обращаться как $args[0].$args[0].<21096818210099

    • <21096888500935> Выход от блока скрипта Игнорируется , потому что Action<T> делегат по определению имеет Нет возврата . <

      • Несмотря на то, что вы можете создавать хост (консоль) выходные данные с помощью Write-Host из блока сценария, такие выходные данные нельзя использовать программно, поскольку они могут обходить потоки вывода PowerShell. ни захватить, ни перенаправить.

Спасибо PetSerAl за важные подсказки в комментариях.

Временные решения:

  • Если блок скрипта, который вы передаете в .ForEach() не должен производить никакого вывода, все, что нужно, это использовать $args[0] вместо <210969085008, хотя вы в своем блоке скрипта8 может по-прежнему использовать один из других обходных путей ниже, чтобы избежать путаницы.

  • Если выход есть необходимо, самое простое решение состоит в преобразовании экземпляра List<T> к массив с .ToArray() первая , на котором .ForEach() работает как положено; упрощенный пример:

    $list = [System.Collections.Generic.List[object]] ('foo', 'bar')
    $list.ToArray().ForEach({ $_ + '!' }) # Note the .ToArray() call.
    

    Вышеизложенное дает 'foo!', 'bar!', как и ожидалось.

    • В качестве альтернативы вы можете использовать:

      • цикл foreach для обработки элементов списка, что означает, что вы должны выбрать имя переменной итерации и ссылаться на нее вместо $_ в теле цикла; например:
        foreach ($itm in $list) { $itm + '!' }
      • или ForEach-Object в конвейере (медленнее, но не требует изменения блока скрипта), как показано в ответе No Refunds No Returns; например:
        $list | ForEach-Object { $_ + '!' }

mklement0
19 декабря 2018 в 15:47
0

Спасибо за этот обходной путь черного пояса, @PetSerAl, но я боюсь, что он слишком сложен и непонятен, чтобы быть практичным.

avatar
Ivan Yang
18 декабря 2018 в 05:43
1

Просто для справки, вы можете использовать этот код для list.ForEach().

$nicInfo.IpConfigurations.ForEach({write-host $args[0].ToString()})

И я сам проверял, работает. Пример кода, как показано ниже:

$s=New-Object System.Collections.Generic.List[string]
$s.Add("hello_1")
$s.Add("hello_2")
$s.Add("hello_3")
$s.ForEach({write-host $args[0].ToString()})

Результаты теста, как показано ниже:

enter image description here

Помимо того, что я нашел похожую проблему, @PetSerAl очень хорошо объяснил там.

mklement0
19 декабря 2018 в 05:01
0

Использование $args[0] Вместо $_ Решает ввод Проблема Не решает Выход Проблема, потому что она печатает непосредственно к хосту < > (консоль), что означает, что вы не сможете ни захватить, ни перенаправить этот вывод.

Ivan Yang
20 декабря 2018 в 02:47
1

@ mklement0, спасибо, что указали на это, я этого не осознавал.

avatar
No Refunds No Returns
18 декабря 2018 в 03:22
1

Вы пытаетесь что-то сделать с каждым элементом коллекции? Вы хотите сделать что-то вроде этого:

$nicInfo.IpConfigurations | ForEach-Object {
  $ipConfiguration = $_
  write-Output $ipConfiguration
  # do more stuff with this $ipConfiguration
}
mklement0
19 декабря 2018 в 05:06
0

ᐩ1 для эффективного обходного решения, хотя это поможет объяснить, почему это необходимо.

mklement0
19 декабря 2018 в 13:31
0

Это не объяснение, потому что PowerShell предлагает .ForEach() метод для всех коллекций (и даже скаляров) в v4+. Единственная причина для обходного пути ForEach-Object заключается в том, что в этом случае конкретный тип коллекции имеет собственный .ForEach() метод, который вытесняет PowerShell. Чтобы увидеть .ForEach() PowerShell в действии, попробуйте (1..3).ForEach({ $_+1 }) или ([collections.arraylist] (1..3)).ForEach({ $_+1 }) или 'foo'.ForEach({ $_+'!' }).