I was poking around in the PowerShell code that defines how PowerShell handles casting. In file LanguagePrimitives.cs, I came across logic similar to the following (here oversimplified and translated into PowerShell).
If ( $TargetObjectType.IsArray ) { <# Do something #> ; break }
If ( $TargetObjectType -eq [Array] ) { <# Do something else #> } |
This confused me. Wouldn’t an array trigger the first set of code and never get to the second If statement? So I did some digging.
It turns out that [Array] is an “abstract” class. That means you can never create an [Array] object. You--or, more accurately, PowerShell--can create a new custom subclass based on the [Array] class, and then create an object of that subclass.
Array-based subclasses specify what type of object the elements of the array will be.
The element type can be specific. In this example, we explicitly ask for an array with [String] elements. PowerShell then creates a new subclass named System.String[] based on the abstract System.Array class. It then creates an object of the new subclass and sets it as the value of the variable.
[string[]]$Names1 = 'Tim', 'Joe'
$Names1.GetType().FullName # yields System.String[] |
If we don’t specify a type for the elements, PowerShell defaults to using [System.Object] for the elements. All object classes are derived directly or indirectly from [System.Object], so we can put whatever types of object we want in default PowerShell arrays.
In this example, PowerShell creates a new subclass named System.Object[].
$Names2 = 'Tim', 'Joe'
$Names2.GetType().FullName # yields System.Object[] |
We can never have an object that is of type [Array], that is, one where $Object.GetType().FullName is “System.Array”. If we tell PowerShell we want an [Array] object, it is interpreted to mean the default subclass Object[].
[array]$Names3 = 'Tim', 'Joe'
$Names3.GetType().FullName # yields System.Object[] |
Custom subclasses based on [Array] have their .IsArray flag set to $True so that .Net and PowerShell know they can do Array related things to them.
$Names1.GetType().IsArray # yields $True
$Names2.GetType().IsArray # yields $True $Names3.GetType().IsArray # yields $True |
The abstract [System.Array] class has the .IsArray flag set to $False, because PowerShell can’t do Array related things to it, because there can’t be an array of type Array.
[System.Array].IsArray # yields $False
|
However, for all arrays, $MyArray -is [Array] yields $True, because -is also checks to see if the class of the object in question is a subclass of the specified class.
[string[]]$Names1 = 'Tim', 'Joe'
$Names1.GetType().FullName # yields System.String[] $Names1.GetType().BaseType.FullName # yields System.Array $Names1.GetType().BaseType.BaseType.FullName # yields System.Object $Names1 -is [System.String[]] # yields $True $Names1 -is [System.Array] # yields $True $Names1 -is [System.Object] # yields $True $Names1 -is [String[]] # yields $True $Names1 -is [Array] # yields $True $Names1 -is [Object] # yields $True |
Looking back on the code in LanguagePrimitives.cs, it now makes sense.
This code block would handle converting to a specified subclass of [Array], such as [System.String[]].
If ( $TargetObjectType.IsArray ) { <# Do something #> ; break }
|
This code block would handle converting to a default [Array], and will result in a [System.Object[]] subclass object. Because [Array] subclasses would be handled by the above code block, only [Array]
If ( $TargetObjectType -eq [Array] ) { <# Do something else #> }
|
No comments:
Post a Comment