Select elements from powershell objects

Viewed 73

Hi everybody!

Short question:

Why does this work:

(Get-CimClass Win32_Share).CimClassMethods['Create'].Parameters

But this doesn’t:

(Get-CimClass Win32_Share).CimClassMethods.Create.Parameters

The same result can be achieved by:

   ((Get-CimClass Win32_Share).CimClassMethods|where-object {$_.name -match 'create'}).parameters

With the same logic I would expect that the same applies for:

(Get-command *help*)['get-help']
Or
(Get-command *help*)['cmdlet']
Or
(Get-childitem)['test.txt']

But it doesn’t. Only the method with where-object is possible here

Other considerations:

I know that this is should be the default way to retrive items from an hashtable and not pscustomobject but I also would like to better understand where else I can use this method. I searched the whole day on google but didn’t find anything.

Thanks in advance Franco

1 Answers

The string-based indexing (['Create']) works, because the Microsoft.Management.Infrastructure.Internal.Data.CimMethodDeclarationCollection type that it is applied to implements a parameterized Item property that has a [string]-typed parameter (abstracted; verify with Get-Member Item -InputObject Get-CimClass Win32_Share).CimClassMethods):

# {get;} indicates that the property is read-only.
Item ParameterizedProperty <T> Item(string key) {get;}

Note: Strictly speaking, for something like ['Create'] to be accepted, it is sufficient for the Item property parameter to be [object]-typed, which is the case for the non-generic [hashtable] type, for instance.

For types with such a method, PowerShell, like C#, supports syntax ['Create'] as syntactic sugar for .Item('Create'), and the implication is that a key-based item lookup is performed.

Typically, such types also implement the IDictionary interface (e.g., [hasthable]), but that isn't the case here.

CimMethodDeclarationCollection behaves similarly to an [ordered] hashtable, in that it supports both positional (e.g, [0]) and key-based indexing (e.g., ['Create']),[1] but with one crucial difference:

  • PowerShell does not enumerate IDictionary-implementing types in the pipeline (you'll have to call .GetEnumerator() to achieve that), whereas CimMethodDeclarationCollection - due to not implementing IDictionary - is enumerated, like other collections such as arrays; that is, its elements / entries are sent one by one through the pipeline.

As for why something like the following doesn't work:

(Get-command help)['get-help']

Get-command *help* outputs multiple objects, which (...) automatically collects in a regular PowerShell array, of type [object[]].

[object[]] (based on System.Array), does not have the requisite parameterized property that would support ['get-help'] syntax - arrays support only positional indexing (e.g. [0] to refer to the first element), based on a parameterized Item property whose parameter is [int]-typed, implemented as part of the IList interface (abstracted; verify with Get-Member Item -InputObject @()):

# Note: IList.Item indicates that the property is implemented
#       as part of the IList interface. 
#       {get;set;} indicates that the property is read-write.   
Item ParameterizedProperty <T> IList.Item(int index) {get;set;}

However, given PowerShell's member-access enumeration feature it is reasonable to expect ['get-help'] to then be applied to the individual elements of the array, given that's how it works with ., the member-access operator (e.g., (Get-Command *help*).Name).
As of PowerShell 7.2.4, however, only ., not also ["someKey"] performs this enumeration; this surprising asymmetry is the subject of GitHub issue #17514.


[1] An [int]-typed key doesn't necessarily imply positional semantics, but does in the context of implementing the IList or ICollection interfaces (or their generic equivalent).

Related