1

I have been working on a scripting project that involves creating custom PowerShell classes. I am currently trying to understand the following syntax

$Temp = [myclass[]]::new(1)

This syntax seems to create a generic List of some kind where I can specify the size of the List. Is there any way for me to modify the constructor for this? Ideally I would like to do something like the following:

[string[]]$ClassData = @('test1','test2','test3')
[myclass[]]$Temp = [myclass[]]::new($ClassData)

And this would call the constructor for myclass for each of the items in $ClassData. Unfortunately this is not the way that it works currently and I have been working around the situation by using the following:

[string[]]$ClassData = @('test1','test2','test3')
[myclass[]]$Temp = @($ClassData | [myclass[]]::new($_))

Any assistance or explanations as to how to make all this work would be greatly appreciated

mklement0
  • 382,024
  • 64
  • 607
  • 775
TimN_FL
  • 67
  • 9

1 Answers1

2

Note: I'm assuming that your working example meant to use [myclass]::new($_), not [myclass[]]::new($_), i.e. that you're creating a single [myclass] instance in each loop iteration: @($ClassData | % { [myclass]::new($_) })


Instead of trying to pass the array of initialization values to the static ::new() method (i.e., to a constructor behind the scenes), cast it:

[myclass[]]$Temp = $ClassData

Note that [myclass[]] is an array of instances of type [myclass], and if [myclass]::new($_) works with $_ representing a single string, then the above cast should succeed.

Essentially, PowerShell will do behind the scenes what your loop-based solution does explicitly.


As for what you tried:

[myclass[]]::new($ClassData) doesn't work, because [array] (System.Array) doesn't have a constructor that accepts an existing array to initialize the new array with; the only constructor is the one that accepts the size (length, element count) of the array, which you can verify as follows:

PS> [object[]]::new

OverloadDefinitions
-------------------
System.Object[] new(int )

Optional reading: casting from hashtables / custom objects:

Casts are very flexible, much more so than in C#, for instance.

Even if the input type is not directly convertible to the target type and the target type doesn't have a single-argument constructor for the input type, PowerShell is still able to construct instances of the target type if all of the following conditions are met:

  • The target type has a public constructor that is parameter-less.[1]
  • The input type has the same set of properties as the target type or a subset of them.
  • All overlapping properties are type-compatible themselves (properties are of the same type or can be converted from the input property type to the target property type).

Here's an example of initializing an array of a custom class (using a PSv5+ class definition) from an array containing a hashtable and a custom object ([pscustomobject]) that each provide a subset of the target type's properties.

# Define a class.
# Not defining a constructor explicitly implicitly defines
# a public, parameter-less one.
class Foo {
  [string] $Bar
  [int]    $Baz
}

# Create an array of [Foo] instances via initialization by a
# hashtable and a custom object.
[Foo[]] $fooArr = @{ Bar = 'None' }, [pscustomobject] @{ Baz = 42 }

Outputting $fooAr afterwards yields:

Bar  Baz
---  ---
None   0
      42

That is, two [Foo] instances were successfully constructed from the input objects.

For more information on casts and type conversions in PowerShell, see this answer.


[1] An approved future enhancement to PowerShell Core will allow initialization from hashtables / custom objects even with constructors that have parameters, as long as there is a matching constructor overload for the input set of hashtable entries / custom-object properties.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • 1
    this is Very awesome. Thanks a lot for all of the info. Now that I know what is going on, I can plan accordingly. My actual implementation doesn't always have a public property that matches the input object. I'm basically taking in an array of JSON objects and use the constructor to rename some of the properties for readability and consolidation. But this definitely puts me on the right path. – TimN_FL Oct 14 '18 at 08:48
  • Glad to hear it, @TimN_FL; my pleasure. – mklement0 Oct 14 '18 at 12:52