代码之家  ›  专栏  ›  技术社区  ›  Martin Prikryl

检查PowerShell中的IEnumerable是否为空

  •  1
  • Martin Prikryl  · 技术社区  · 6 年前

    是否有本机PowerShell方法来测试,如果 IEnumerable 是空的吗?

    我知道我可以打电话 Linq.Enumerable.Any 这样地:

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

    但我希望有一种更本土的方式。

    1 回复  |  直到 6 年前
        1
  •  1
  •   mklement0    6 年前

    不幸的是, 从Windows PowerShell v5.1/PowerShell Core v6.1起的PowerShell本机方式 和while [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 & {} (计算为“空集合”)do 实施 [IEnumerable] ( [System.Collections.IEnumerable] 或者一个通用的对应物),但是在PowerShell的世界里 一切 是可枚举的,偶数 标量 而且,在 管道 -但不是和 foreach (!)-甚至 零值 . 值得注意的例外是“空集合”( [System.Management.Automation.Internal.AutomationNull]::Value )唯一的目的是表明 没有什么 列举(你可以这样认为,作为一个特殊的列举案例,它 应该 实施 [IEnumerable] )

    • 然而, 1, 2, 3 实施 [IEnumerable] :它是类型为的常规PowerShell数组 [object[]] ;你可以用一个明确的 [对象[] CAST,显然不是一个通用的解决方案,因为它可以转换为 数组 强制完全枚举-有关详细信息,请参阅底部部分。

    一个强大但笨重的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 管道 执行懒惰的评估)这方面是 迷路的 当您在 变量 ,因为PowerShell 收集 静态输出 数组 ( [对象[] )

    因此,a 本机PowerShell解决方案 会是 假设 Test-Any CMDLE 那就是:

    • 通过接收输入 管道 ,逐对象。
    • 输出 $True 如果至少收到一个输入对象。

    但是,从Windows PowerShell v5.1/PowerShell Core v6.1开始,没有办法 按需停止管道 ,其中 测试任意 为了有效地工作(为了防止完全枚举),需要执行Cmdlet:

    • 可以找到一个长期存在的功能请求 on GitHub .

    • 有效率的 测试任意 通过使用 私有的 PowerShell类型 可以在中找到 this answer 我的,承蒙 PetSerAl ;除了依赖私有类型外,还将对 第一 在会话中使用。


    可选阅读:使用 [Linq.Enumerable]::Any($enumerable) 使用PowerShell阵列

    1,2,3 (通常表示为 @(1, 2, 3) 但这是不必要的)是 [对象[] 数组,PowerShell的默认数组类型。

    我不清楚你为什么不能把这些数组传递给 .Any() 正如 ,因为它确实与 同一类型 : [Linq.Enumerable]::Any([object[]] (1,2,3)) # OK

    收集 命令 的多对象输出还(隐式)创建 [对象[] 数组,但是, 可以 按原样通过: [Linq.Enumerable]::Any((Get-ChildItem)) # OK

    最后,使用 具体的 类型也可以按原样传递:
    $intArr = [int[]] (1, 2, 3); [Linq.Enumerable]::Any($intArr) # OK

    如果有人知道为什么不能直接使用 [对象[] 在此上下文中数组,以及是否有充分的理由,请告诉我们。