Creating Generic Types in PowerShell

Fri, Aug 18, 2006 2-minute read

[Edit 02/01/2012 - PowerShell added support for this in Version 2 via the following syntax:]

PS > $r = New-Object "System.Collections.Generic.List[Int]"
PS > $r.Add(10)
PS > $r.Add("Hello")
Cannot convert argument "item", with value: "Hello", for "Add" to type "System.Int32"
(...)
At line:1 char:1
+ $r.Add("Hello")
+ ~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

(Original Post)

Although the New-Object cmdlet is powerful, it doesn’t yet handle creating generic types very elegantly.  For a simple parameterized type, you can use the syntax that the .Net framework uses under the hood:  

New-Object “System.Collections.ObjectModel.Collection``1[System.Int32]”

However, that begins to fall apart if you want to use types defined outside of the mscorlib DLL, or want to create complex generic types (for example, ones that refer to other generic types.)  

The following script, New-GenericObject, creates objects of generic types.

## New-GenericObject.ps1
## Creates an object of a generic type:
##
## Usage:
##
##   # Simple generic collection
##   $list = New-GenericObject System.Collections.ObjectModel.Collection System.Int32
##
##   # Generic dictionary with two types
##   New-GenericObject System.Collections.Generic.Dictionary System.String,System.Int32
##
##   # Generic list as the second type to a generic dictionary
##   $secondType = New-GenericObject System.Collections.Generic.List Int32
##   New-GenericObject System.Collections.Generic.Dictionary System.String,$secondType.GetType()
##
##   # Generic type with a non-default constructor
##   New-GenericObject System.Collections.Generic.LinkedListNode System.Int32 10
##
param(
[string] $typeName = $(throw "Please specify a generic type name"),
[string[]] $typeParameters = $(throw "Please specify the type parameters"),
[object[]] $constructorParameters
)

## Create the generic type name
$genericTypeName = $typeName + '`' + $typeParameters.Count
$genericType = [Type] $genericTypeName

if(-not $genericType)
{
    throw "Could not find generic type $genericTypeName"
}

## Bind the type arguments to it
[type[]] $typedParameters = $typeParameters
$closedType = $genericType.MakeGenericType($typedParameters)
if(-not $closedType)
{
    throw "Could not make closed type $genericType"
}

## Create the closed version of the generic type
,[Activator]::CreateInstance($closedType, $constructorParameters)

[Edit: Bruce Payette pointed out that casting a string to a [Type] does all the hard work of the previous GetType function I wrote.  Also fixes a problem when you create generic types that PowerShell attempts to enumerate over when you return them.]