Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Just get the type of Powershell Object [collection?] (not the methods etc) (also: what's going on here?)

Tags:

powershell

I am setting the variable 'a' like this:

$a=dir -recurse c:\temp

If I now examine this object with 'get-member' like this:

$a|get-member

I get back the type, but also all the methods and other properties like this:

   TypeName: System.IO.FileInfo

Name                      MemberType     Definition                                                                                            
----                      ----------     ----------                                                                                            
Mode                      CodeProperty   System.String Mode{get=Mode;}                                                                         
AppendText                Method         System.IO.StreamWriter AppendText()                                                                   
CopyTo                    Method         System.IO.FileInfo CopyTo(string destFileName), System.IO.FileInfo CopyTo(string destFileName, bool...
[...]

Which is nice; but sometimes I just want to get hold of the type (I'll look up whatever it does afterwards).

So I tried this:

$a|get-member|select-object -Property typename

The output suprised me initially: because what you get back is the typename for each individual item in the collection- and the types (although clearly related) are not identical:

TypeName
--------
System.IO.DirectoryInfo
System.IO.DirectoryInfo
System.IO.DirectoryInfo
[...]
System.IO.FileInfo
[...]

Then I thought about this; and it sort of made sense - this is a collection of Objects that I'm piping through the Object-Pipeline; but then it made me think:

  1. What was 'Get-Member' actually telling me previously ? When it said the type was 'System.IO.FileInfo' - but actually the collection contains a mixture of object types ?

  2. Whatever it is 'Get-Member' is displaying - how do I get at that exact thing ?

I can almost (sort-of, but actually its just wrong) get what I thought I was initially after with this:

$a|get-member|select-object -Property typename -first 1

But this just peeks at the 'first' object; and in fact gives me a different answer to what 'Get-Member' output for me.

So what is the 'TypeName' that 'Get-Member' is showing- and where is that stored ?

Is the value of 'dir' (Get-ChildItem against a filepath) simply a collection of objects, or is it a parent object (with its own set of 'scalar' properties) and a single property referencing a collection of associated objects ?

like image 341
monojohnny Avatar asked Sep 08 '25 04:09

monojohnny


1 Answers

That's a lot of questions in one, let's see if we can make some sense of this.

The TypeName that Get-Member displays for each distinct type of object (we'll get back to that), comes from a hidden property that all objects in PowerShell carry, called pstypenames:

PS C:\> $something = 1
PS C:\> $something.pstypenames
System.Int32
System.ValueType
System.Object
PS C:\>

So, pstypenames is an ordered list of all the types in that objects type hierarchy. If we change the value of pstypenames, you'll see that reflected in the output from Get-Member:

PS C:\> $something.pstypenames.Insert(0,"MonoJohnny.CustomTypeName")
PS C:\> Get-Member -InputObject $something


   TypeName: MonoJohnny.CustomTypeName

Name        MemberType Definition
----        ---------- ----------
...

So, if you want the TypeName for an object, as displayed by Get-Member, you can always do:

$something.pstypenames[0]

As shown above, this value can be manipulated, so if you want the true type of an object at runtime, use the GetType() method:

$something.GetType().FullName

The reason that Get-Member only shows you the entire list of properties for a System.IO.FileInfo object once is that it (rightly so) assumes that all other objects of type System.IO.FileInfo will have the exact same members - no need to duplicate that output.

I you have multiple distinct types in a collection and pipe those to Get-Member, it'll only show you the members for the first object it encounters with a unique type name (remember, the value of pstypenames[0]). This is the case when you pipe Get-ChildItem to Get-Member, since Get-ChildItem on the filesystem provider only returns two different types of objects - FileInfo objects and DirectoryInfo objects.


For builtin types this is totally fine, but with custom objects that you create in PowerShell, this can be quite annoying.

Consider the following example:

PS C:\> $Object1 = New-Object psobject -Property @{ Prop1 = 1 }
PS C:\> $Object2 = New-Object psobject -Property @{ Prop2 = 2 }

Now, $Object1 and $Object2 are clearly 2 different kinds of objects - they have different property names. But what happens when we pipe them to Get-Member in the same pipeline:

PS C:\> $Object1,$Object2 |Get-Member


   TypeName: System.Management.Automation.PSCustomObject

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Prop1       NoteProperty System.Int32 Prop1=1

Since the underlying type of both objects are System.Management.Automation.PSObject, the value of pstypenames is also the same for both, and Get-Member can't distinguish between the two.

Now, all of a sudden, the ability to manipulate pstypenames without actually fiddling with the type system comes in handy:

PS C:\> $Object1.pstypenames.Insert(0,"ObjectType1")
PS C:\> $Object2.pstypenames.Insert(0,"ObjectType2")
PS C:\> $Object1,$Object2 |Get-Member


   TypeName: ObjectType1

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Prop1       NoteProperty System.Int32 Prop1=1


   TypeName: ObjectType2

Name        MemberType   Definition
----        ----------   ----------
Equals      Method       bool Equals(System.Object obj)
GetHashCode Method       int GetHashCode()
GetType     Method       type GetType()
ToString    Method       string ToString()
Prop2       NoteProperty System.Int32 Prop2=2

Sweet!


You can also inject a custom type name during object creation by specifying PSTypeName as a string property:

PS C:\> $Object1 = New-Object psobject -Property @{ Prop1 = 1; PSTypeName = 'ObjectType1' }
PS C:\> $Object2 = New-Object psobject -Property @{ Prop2 = 2; PSTypeName = 'ObjectType2' }
like image 179
Mathias R. Jessen Avatar answered Sep 10 '25 01:09

Mathias R. Jessen