powershelltypescastingtype-coercion

Powershell: coerce or cast to type as named in a string variable


I am trying to use the method outlined here to create a number of custom data types, and rather than having a line for each I would like to define a hash table of the names and types like this

$pxAccelerators = @{
   pxListObject = '[System.Collections.Generic.List[Object]]'
   pxListString = '[System.Collections.Generic.List[String]]'
   pxOrderedDictionary = '[System.Collections.Specialized.OrderedDictionary]'
}

Then I could use something like this

$typeAccelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
foreach ($key in $pxAccelerators.Keys) {
    $name = $key
    $type = $pxAccelerators.$key
    $typeAccelerators::Add($key,$type)
}

to loop through the hash table and add each one. However, the issue of course is that $type isn't an actual type, it's a string. And $typeAccelerators::Add($key,$type) needs a string and an actual type. So basically I need to coerce a string like '[System.Collections.Specialized.OrderedDictionary]' to the actual type. I have found plenty of references to casting or coercing from one data type to another, but I can't seem to find any reference to how to convert a string to a type as defined BY the string. I have tried all these stabs in the dark

([System.Type]'[System.Collections.ArrayList]')::new()
[System.Type]'[System.Collections.ArrayList]'
[System.Type]'[System.Collections.ArrayList]' -as [System.Type]
'[System.Collections.ArrayList]' -as ([PowerShell].Assembly.GetType('[System.Collections.ArrayList]')

to no avail. $type = ([PowerShell].Assembly.GetType('[System.Collections.ArrayList]')) seems to work, in that it doesn't throw an exception. But $type.GetType() does throw You cannot call a method on a null-valued expression.. Interestingly, auto completion with [PowerShell].Assembly.GetType('[System.Collections.ArrayList]'). shows properties like BaseType and FullName are available, suggesting that I have actually produced a type, but using .GetType() on the result throws an exception. I tried

$pxAccelerators = @{
   pxListObject = 'System.Collections.Generic.List[Object]'
   pxListString = 'System.Collections.Generic.List[String]'
   pxOrderedDictionary = 'System.Collections.Specialized.OrderedDictionary'
}
$typeAccelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
foreach ($key in $pxAccelerators.Keys) {
    $name = $key
    $type = [PowerShell].Assembly.GetType($pxAccelerators.$key)
    $typeAccelerators::Add($key,$type)
}

[PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::Get

and the accelerators are being added, but the type to be acclerated is not there, suggesting that the GetType() line is not actually producing a type.

Lastly, I found this which seems to be getting closer. But I can't seem to rock how to access the method without starting from some sort of type already, and [System.Type].GetType('System.Int32') is throwing, so that seems to be a dead end.

Am I trying to do something impossible? Or just missing the proper mechanism?


Solution

  • -as [type] will do

    The -as operator gladly takes a type name as it's right-hand side operand.

    Change the values of your dictionary to contain just a valid type name, and it becomes as easy as:

    $pxAccelerators = @{
       pxListObject = 'System.Collections.Generic.List[Object]'
       pxListString = 'System.Collections.Generic.List[String]'
       pxOrderedDictionary = 'System.Collections.Specialized.OrderedDictionary'
    }
    
    $typeAccelerators = [PowerShell].Assembly.GetType("System.Management.Automation.TypeAccelerators")
    foreach ($acc in $pxAccelerators.GetEnumerator()) {
        $name = $acc.Key
        $type = $acc.Value -as [type]
        $typeAccelerators::Add($name,$type)
    }
    

    Result:

    PS ~> [pxOrderedDictionary] -is [type]
    True
    PS ~> [pxOrderedDictionary].FullName
    System.Collections.Specialized.OrderedDictionary