15

Well generics in Powershel are quite confusing. To instantiate a simple List you need to dance around with a tambourine:

$type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
$type= $type.MakeGenericType("System.string" -as "Type")
$o = [Activator]::CreateInstance($type)

But what if I need something a bit more complex like: <Dictionary<string,List<Foo>> for example

or for example here: Dictionary<string,List<string>>

$listType = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
$listType = $listType.MakeGenericType("System.string" -as "Type")
$L = [Activator]::CreateInstance($listType)

$dicType = ("System.Collections.Generic.Dictionary"+'`'+"2") -as "Type"

#the next line is problematic
$dicType = $dicType.MakeGenericType( 
     @( ("system.string" -as "Type"), 
        ("System.Collections.Generic.List" as "Type)) # and that's of course wrong
      )

$D = [Activator]::CreateInstance($dicType )
iLemming
  • 34,477
  • 60
  • 195
  • 309
  • Is there some reason the built in HashTable won't work for what you are trying to do? – EBGreen Aug 15 '12 at 18:57
  • well it my work for that particular example, but that's not my point, what if I need: List> – iLemming Aug 15 '12 at 19:10
  • I think the decision to keep generics difficult to implement was a conscious one. Powershell is first and foremost an Administrative Scripting language. It just happens to have access to the .Net framework. If you need complex generic types, that is probably outside the intent. – EBGreen Aug 15 '12 at 19:14
  • I'm kinda stuck. Started writing the script and in the end I realized that I need to use complex generic type... And I was so close to finish that thing :( – iLemming Aug 15 '12 at 19:16
  • For what it is worth, I'm not saying that to be flippant, I have wrestled with generics in the past and have come to the opinion that if your code **requires** complex generics, just move to C# or another of the compiled languages and save yourself some headache – EBGreen Aug 15 '12 at 19:18
  • 2
    http://stackoverflow.com/questions/184476/powershell-generic-collections – EBGreen Aug 15 '12 at 19:24
  • I use that method for dictionaries as well, EBGreen. For more complex stuff I wrap c# code in a class in Add-Type @' .... code .... '@ and access objects from there with New-Object – Chris N Aug 15 '12 at 19:45
  • it's not that hard once you know the syntax. you don't need reflection. see my answer below. – x0n Aug 15 '12 at 21:02

5 Answers5

28

While you can delve into CLR internal representations and make life hard for yourself, you don't have to:

$dict = new-object 'collections.generic.dictionary[string,int]'
$dict.add("answer", 42)

Want a type literal representation?

[collections.generic.dictonary[string,int]]

Done. How about generic type parameters?

$dictOfList = new-object 'collections.generic.dictionary[string,
    [collections.generic.list[int]]]'

Done.

However, there's an unfortunate catch. In PowerShell 2.0, there's a bug when you mix and match BCL and 3rd party types as type parameters. The latter need to be assembly qualified:

# broken over two lines for clarity with backtick escape
$o = new-object ('collections.generic.dictionary[[{0}],[{1}]]' -f `
        [type1].fullname, [type2].fullname)

Hope this helps. In PowerShell 3.0, this has been fixed.

x0n
  • 51,312
  • 7
  • 89
  • 111
1

Yeah, seems it's possible, but as almost everything else in PS, this is darn ugly too. Here's the real world example:

$requestItemsType is a Dictionary<string, List<Amazon.DynamoDB.Model.WriteRequest>>

$wrt = ("System.Collections.Generic.List``1" -as "Type") 
$wrt = $wrt.MakeGenericType( @( ("Amazon.DynamoDB.Model.WriteRequest" -as "Type")))

$requestItemsType = ("System.Collections.Generic.Dictionary"+'`'+"2") -as "Type"
$requestItemsType = $requestItemsType.MakeGenericType( @( ("System.string" -as "Type"), ($wrt)))
$ri = [Activator]::CreateInstance($requestItemsType)
$ri.Add("TaskLog",$writeRequests)
iLemming
  • 34,477
  • 60
  • 195
  • 309
0

The example above doesn't have to be as complicated if you're creating a dictionary with a custom type defined:

$propertiesType = ("System.Collections.Generic.Dictionary"+'`'+"2") -as "Type"
$propertiesType = $propertiesType.MakeGenericType( @( ("System.string" -as "Type"), ("Namespace.CustomType" -as "Type")))
$properties = [Activator]::CreateInstance($propertiesType)
0

I know this is perhaps a bit old, but this workaround appears to work nicely in PowerShell 2 and can pretty much be used as a direct replacement.

$foo = [activator]::CreateInstance(([System.Collections.Generic.List[string]] -as 'Type'))
0

Weird. I've always just done like this:

$dict = [system.collections.generic.dictionary[string, int]]::new()

$dict.add("my string", 42)

I always thought it was odd seeing people use New-Object and other unnecessary stuff when the constructor overloads are all exposed in a much more convenient way.

Though I only started with PS 3.0, admittedly.

Dragoon
  • 723
  • 6
  • 13