Creating Generic Types in PowerShell

[Edit 02/01/2012 – PowerShell added support for this in Version 2]

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

 

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.]

5 Responses to “Creating Generic Types in PowerShell”

  1. hg writes:

    The above code throws the following exception. ANY CLUE????

    Exception calling "CreateInstance" with "2" argument(s): "Ambiguous match found
    ."
    At F:\Test\test.ps1:37 char:29
    + ,[Activator]::CreateInstance( <<<< $closedType, $constructorParameters)
    PS F:\Test> .\parser.ps1 "BuildLog.txt" "out.txt"

  2. Lee Holmes writes:

    Hi hg;

    This has been resolved after CTP3 — builds of Win7 have it corrected, as well as the most recent preview release for other OSs: https://connect.microsoft.com/windowsmanagement/Downloads

  3. kim writes:

    Hi Lee.

    Are you ok if I use this in a project of mine?
    The project and the source will be accessible from my blog.

    Thanks.

  4. Lee Holmes writes:

    Absolutely! By the way, if all you need is to create an instance of a generic type (as opposed to invoke a generic method on a non-generic type), PowerShell V2 now supports that directly:

    $list = New-Object System.Collections.ObjectModel.Collection[System.Int32]

  5. Using PowerShell for SNMP polling | vBlog writes:

    […] haven’t always worked in PowerShell, supposedly they do now, but I’ve needed to use a workaround provided by Lee Holmes to gain any success (please let me know if there’s an easy way of doing […]

Leave a Reply