没有'.Count'属性的对象-使用@()(数组子表达式运算符)与[Array]类型转换 [英] Objects with no '.Count' Property - use of @() (array subexpression operator) vs. [Array] cast

查看:124
本文介绍了没有'.Count'属性的对象-使用@()(数组子表达式运算符)与[Array]类型转换的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试执行一些简单的if语句,但是所有基于[Microsoft.Management.Infrastructure.CimInstance]的较新cmdlet似乎都没有公开.count方法?

I am trying to perform some simple if statements, but all of the newer cmdlets that are based upon [Microsoft.Management.Infrastructure.CimInstance] don't seem to expose a .count method?

$Disks = Get-Disk
$Disks.Count

不返回任何内容.我发现可以将其强制转换为[array],这使其按预期返回.NET .count方法.

Doesn't return anything. I found that I can cast this as an [array], which makes it returns a .NET .count method as expected.

[Array]$Disks = Get-Disk
$Disks.Count

此方法无需将其直接转换为先前cmdlet的数组即可.

This works without directly casting it as an array for previous cmdlets:

(Get-Services).Count

解决此问题的推荐方法是什么?

What is the recommended way to get around this?

一个无效的示例:

$PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
  If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
   Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
   Else If ($PageDisk.Count -eq 1) { Do X }

选项A(以数组形式广播):

Option A (Cast as Array):

[Array]$PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
  If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
   Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
   Else If ($PageDisk.Count -eq 1) { Do X }

选项B(使用数组索引):

Option B (Use Array Indexes):

 $PageDisk = Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)}
  If ($PageDisk[0] -eq $Null) {Write-Host "No suitable drives."; Continue}
   Else If ($PageDisk[1] -ne $Null) {Write-Host "Too many drives found, manually select it."}
   Else If (($PageDisk[0] -ne $Null) -and (PageDisk[1] -eq $Null)) { Do X }

选项C(数组)-感谢@PetSerAl:

Option C (Array) -Thanks to @PetSerAl :

$PageDisk = @(Get-Disk | Where {($_.IsBoot -eq $False) -and ($_.IsSystem -eq $False)})
  If ($PageDisk.Count -lt 1) {Write-Host "No suitable drives."; Continue}
   Else If ($PageDisk.Count -gt 1) {Write-Host "Too many drives found, manually select it."}
   Else If ($PageDisk.Count -eq 1) { Do X }

基于CIM的cmdlet不公开.Count方法的原因是什么?建议的处理方式是什么?选项B在我看来是令人费解的,而且很难阅读.选项A可行,但是powershell难道不应该将它强制转换为数组吗?我会以完全错误的方式进行此操作吗?

What is the reason for CIM based cmdlets not exposing the .Count method? What is the recommended way to handle this? Option B seems convoluted to me, and hard to read. Option A works, but shouldn't powershell cast this as an array for me? Am I going about this in entirely the wrong way?

推荐答案

在PSv3 +中,通过统一处理标量和集合,任何对象-甚至$null- 应该具有.Count属性(并且,除了$null之外,还应该支持使用[0]进行索引编制.)

In PSv3+, with its unified handling of scalars and collections, any object - even $null - should have a .Count property (and, with the exception of $null, should support indexing with [0]).

任何支持上述内容的对象 都应视为 bug .

Any occurrence of an object not supporting the above should be considered a bug.

例如,未按这些规则播放的[pscustomobject]实例是已知错误.

For instance, [pscustomobject] instances not playing by these rules is a known bug.

由于我不知道上述错误是否与Get-Disk输出的[Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk]实例有关,并且由于Get-Disk(至少当前)仅在 Windows PowerShell 中可用,我鼓励您在 uservoice.com 上提交单独的错误.

Since I don't know if said bug is related to the [Microsoft.Management.Infrastructure.CimInstance#ROOT/Microsoft/Windows/Storage/MSFT_Disk] instances that Get-Disk outputs, and since Get-Disk - at least currently - is only available in Windows PowerShell, I encourage you to file a separate bug on uservoice.com.

仅需 使用数组子表达式运算符@(...) :

Use of array-subexpression operator @(...) is only necessary:

  • 作为上述错误的 解决方法 .

  • as a workaround for the bug above.

,以防标量对象具有其拥有 .Count属性.

in case a scalar object happens to have its own .Count property.

通常,如果您确实需要确保某些东西是数组,请使用@(...)而不是[Array] .../[object[]] ... -@()是PowerShell -惯用,简洁和语法上更容易.

Generally, if you do need to ensure that something is an array, use @(...) rather than [Array] ... / [object[]] ... - @() is PowerShell-idiomatic, more concise, and syntactically easier.

也就是说,鉴于@()技术上会为现有阵列创建(浅)副本,在处理潜在的大型阵列时,您可能更喜欢[Array].

That said, given that @() technically creates a (shallow) copy of an existing array, you may prefer [Array] when dealing with potentially large arrays.

此外, @(...)[Array] ... 通常不等效 ,例如

Additionally, @(...) and [Array] ... are not generally equivalent, as PetSerAl's helpful examples in a comment on the question demonstrate; to adapt one of his examples:

@($null)返回一个单项数组,其唯一元素为$null,而[Array] $null不起作用(保持$null).

@($null) returns a single-item array whose one and only element is $null, whereas [Array] $null has no effect (stays $null).

@()的这种行为与其目的一致(请参见下文):由于$null不是数组,因此@()将其包装在一个数组中(结果是[System.Object[]]实例,其中$null是唯一的实例)元素).

This behavior of @() is consistent with its purpose (see below): since $null is not an array, @() wraps it in one (resulting in a [System.Object[]] instance with $null as the only element).

在PetSerAl的其他示例中,@()New-Object创建的数组和集合的行为-可能令人惊讶-见下文.

In PetSerAl's other examples, @()'s behavior with New-Object-created arrays and collections - may be surprising - see below.

@()目的,

The purpose of @(), the array-subexpression operator, is, loosely speaking, to ensure that the result of an expression/command is treated as an array, even if it happens to be a scalar (single object).

重要:@()不会构造数组;相反,它保证结果是一个;因此,例如@( @( 1 ) )@( 1 ), 1相同.

Important: @() does not construct arrays; instead it guarantees that the result is one; therefore, @( @( 1 ) ) is the same as @( 1 ) and , 1 for instance.

更准确地说,@()的行为如下:向PetSerAl献上帽子的提示,以提供广泛的帮助.

More accurately, @() behaves as follows: Tip of the hat to PetSerAl for his extensive help.

  • In PSv5.1+ (Windows PowerShell 5.1 and PowerShell [Core] 6+), using an expression that directly constructs an array using ,, the array constructor operator, optimizes @() away:

  • 例如,@(1, 2)1, 2相同,而@(, 1), 1相同.

对于使用 just ,构造的数组-产生System.Object[]数组-此优化很有用,因为它节省了先展开该数组的不必要步骤然后重新包装(请参见下文).
据推测,这种优化是由于使用@( ..., ..., ...)构造数组的广泛且先前效率低下的做法所促成的,这是基于错误的信念,即需要使用@()来构造数组.

In the case of an array constructed with just , - which yields a System.Object[] array - this optimization is helpful, because it saves the unnecessary step of first unrolling that array and then repackaging it (see below).
Presumably, this optimization was prompted by the widespread and previously inefficient practice of using @( ..., ..., ...) to construct arrays, stemming from the mistaken belief that @() is needed to construct an array.

但是,在 Windows PowerShell v5.1中,仅 中,优化是意外地,当使用以下命令构造数组时使用 cast 特定类型,例如[int[]](在 PowerShell [Core] 6+和更早版本的Windows PowerShell中,此行为已得到纠正版本不受 的影响);例如,
@([int[]] (1, 2)).GetType().Name产生Int32[].这是@()返回除System.Object[]以外的 的唯一情况,并假定它总是会导致意外的错误和副作用;例如:
@([int[]] (1, 2))[-1] = 'foo'中断.
$a = [int[]] (1, 2); $b = @([int[]] $a)意外地未创建 new 数组-请参见
此GitHub问题.

However, in Windows PowerShell v5.1 only the optimization is unexpectedly also applied when constructing an array with a specific type using a cast, such as [int[]] (the behavior has been corrected in PowerShell [Core] 6+ and older Windows PowerShell versions are not affected); e.g.,
@([int[]] (1, 2)).GetType().Name yields Int32[]. This is the only situation in which @() returns something other than System.Object[], and assuming that it always does can lead to unexpected errors and side effects; e.g.:
@([int[]] (1, 2))[-1] = 'foo' breaks.
$a = [int[]] (1, 2); $b = @([int[]] $a) unexpectedly doesn't create a new array - see this GitHub issue.

否则::如果@(...)中的(第一个)语句是表达式,而恰好是集合,则集合是枚举的;按原样收集命令的(通常是一对一的流)输出;无论哪种情况,最终的对象数量都决定了行为:

Otherwise: If the (first) statement inside @(...) is an expression that happens to be a collection, that collection is enumerated; a command's (typically one-by-one streaming) output is collected as-is; in either case the resulting count of objects determines the behavior:

  • 如果结果是单个项目/包含 no 个项目,则结果将包装在一个元素/类型为[System.Object[]] 的空数组.

  • If the result is a single item / contains no items, the result is wrapped in a single-element / empty array of type [System.Object[]].

  • 例如,@('foo').GetType().Name产生Object[],而@('foo').Count产生1(尽管如上所述,在PSv3 +中,您可以直接使用'foo'.Count). @( & { } ).Count产生0(执行空脚本块将输出空集合" ([System.Management.Automation.Internal.AutomationNull]::Value)

  • E.g., @('foo').GetType().Name yields Object[] and @('foo').Count yields 1 (though, as stated, in PSv3+, you can use 'foo'.Count directly).
    @( & { } ).Count yields 0 (executing an empty script block outputs a "null collection" ([System.Management.Automation.Internal.AutomationNull]::Value)

注意事项: @()New-Object调用周围,该调用创建一个数组/集合输出,该数组/集合输出包装在单元素外部数组中 .

Caveat: @() around a New-Object call that creates an array / collection outputs that array/collection wrapped in a single-element outer array.

  • @(New-Object System.Collections.ArrayList).Count产生1-空数组列表包装在单个元素System.Object[]实例中.
  • 原因是New-Object由于是命令(例如 cmdlet 调用)而不是 可能会解包,从而导致@()仅看到一个单个项(恰好是一个数组/集合),因此将其包装在一个单项数组中.

  • @(New-Object System.Collections.ArrayList).Count yields 1 - the empty array list is wrapped in a single-element System.Object[] instance.
  • The reason is that New-Object, by virtue of being a command (such as a cmdlet call) is not subject to unwrapping, causing @() to see only a single item (which happens to be an array/collection), which it therefore wraps in a single-item array.

可能令人困惑的是,当您使用 expression 构造数组/集合时,这不会发生 ,因为表达式的输出是展开(展开):

What may be confusing is that this does not happen when you use an expression to construct an array / a collection, because the expression's output is unwrapped (unrolled):

  • @([system.collections.arraylist]::new()).Count产生0;表达式将输出一个空集合,该集合将展开为不包含任何项目的结果,该结果将@()重新打包为空的System.Object[]数组.
    请注意,在 PSv3 + 中,只需在New-Object中使用额外的括号((...))-将New-Object 命令转换为表达式-将产生相同的结果:
    @((New-Object System.Collections.ArrayList)).Count也会产生0.
  • @([system.collections.arraylist]::new()).Count yields 0; the expression outputs an empty collection that is unrolled to a result containing no items, which @() repackaged as an empty System.Object[] array.
    Note that, in PSv3+, simply using an extra set of parentheses ((...)) with New-Object - which converts the New-Object command to an expression - would yield the same result:
    @((New-Object System.Collections.ArrayList)).Count yields 0 too.

如果结果包含多个 个项目,则这些项目将作为常规PowerShell数组([System.Object[]])返回;例如:

If the result comprises multiple items, these items are returned as a regular PowerShell array ([System.Object[]]); e.g.:

  • 使用命令:
    • $arr = @(Get-ChildItem *.txt)System.Object[]数组中收集来自Get-ChildItem的一对一流输出
    • With a command:
      • $arr = @(Get-ChildItem *.txt) collects the one-by-one streaming output from Get-ChildItem in a System.Object[] array

      具有表达式:

      • $arr = [int[]] (1, 2); @($arr) 枚举 [int[]]数组$arr,然后重新打包元素为System.Object[]数组.

      • $arr = [int[]] (1, 2); @($arr) enumerates [int[]] array $arr and then repackages the elements as a System.Object[] array.

      请注意此过程的效率低下类型保真度的潜在损失:原始数组被枚举并收集在 new 数组,始终为System.Object[]类型; 有效的替代方案是强制转换为[array] 的方法(也可用于命令):[array] $result = ...

      Note the inefficiency and potential loss of type fidelity of this process: the original array is enumerated and collected in a new array that is always of type System.Object[]; the efficient alternative is to cast to [array] (which also works with commands): [array] $result = ...

      这篇关于没有'.Count'属性的对象-使用@()(数组子表达式运算符)与[Array]类型转换的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆