1

Lets assume we have a module called ImportTest like this:

# ~\Modules\ImportTest\ImportTest.psm1
. ("{0}\TestClass.ps1" -f $PSScriptRoot)
# skip Export-ModuleMember because there is no explicit way to export classes

# ~\Modules\ImportTest\TestClass.ps1
Class TestClass {
    [string] $OS
    [string] $Name
}

As I know from this, it is possible to use the keyword using to load a module with classes. So I tried this:

using module ImportTest
# skip Import-Module ImportTest because it does not load classes. 
$myclass = [TestClass]::new()

But it still doesn't find the definition. If I define the class in the script module file (ImportTest.psm1) itself it works.

Did I miss anything? - As I know there is no other way to do the dotsourcing or to export it in a other scope.

Community
  • 1
  • 1
OCram85
  • 841
  • 1
  • 9
  • 25

1 Answers1

1

The way you are trying to do it is the way I think it should work, but PowerShell 5.0 and 5.1, at least, don't work that way. For what it's worth, the using keyword hasn't made it into production code on my projects because it has always exhibited some kind of absurd behavior. There are some bug reports around this so we might see some improvement in 6.0. I've seen two options to work around this.

Implement a Function that Returns Your Class

# ~\Modules\ImportTest\TestClass.ps1
Class TestClass {
    [string] $OS
    [string] $Name
}

function New-TestClass { return [TestClass]::new()}

Then,

Import-Module ImportTest
New-TestClass

to get an instance of TestClass.

The downside is that your module code becomes littered with code related to exporting classes.

Invoke New-Object inside a Bound Script Block

You can use a bound script block to get an instance of TestClass:

(Get-Module ImportTest).NewBoundScriptBlock({[TestClass]::new()})

If that's too ugly, you could write a utility function something like this:

function New-Object2
{
    param($FullyQualifiedName)
    $moduleName,$className = $FullyQualifiedName.Split('\')
    & (Get-Module $moduleName).NewBoundScriptBlock(
        [scriptblock]::Create("[$className]::new()")
    )
}

Then,

New-Object2 ImportTest\TestClass

returns an instance of the TestClass object.

alx9r
  • 3,675
  • 4
  • 26
  • 55
  • 1
    Thanks for the help. Meanwhile I solved my problem while using the `ScriptsToProcess` parameter in a module manifest to load the class itself in the parent script scope :) – OCram85 Mar 02 '17 at 09:07
  • Unfortunately it seems like there aren't any convincing class related improvements ins PS 5.1 to work with classes in a production environment. – OCram85 Mar 02 '17 at 09:09
  • 5.1 did improve a few edge cases involving classes. I'm using classes in some modules in production. If you have the test coverage ordinarily warranted by production code, PowerShell classes are fine even in 5.0 -- classes just might not behave the way you first expect them to. Much of PowerShell doesn't behave the way I first expected it to, which means I have rather high test coverage anyway. Using classes doesn't really change that calculus. And classes allow for some refactorings that dramatically reduce line count, so they're a pretty big win on balance for the projects I've tackled. – alx9r Mar 02 '17 at 11:29
  • I really would like to use the classes, because it really speeds up creating custom data structures. Using PSCustomObjects and giving them unique type names isnt' alway the best solution. But one big downside ist the lack of _advanced functions_ in class methods. – OCram85 Mar 02 '17 at 13:20