My project is structured like this:
MyScript.ps1
classes\
Car.ps1
Tesla.ps1
Car.ps1 is the base class of Tesla.ps1. I attempt to define Tesla like this in Tesla.ps1:
. "$PSScriptRoot\Car.ps1"
class Tesla : Car
{
}
MyScript.ps1 needs to use the Tesla class, but shouldn't need to know that it inherits from Car.
. "$PSScriptRoot\classes\Tesla.ps1"
$tesla = [Tesla]::new()
Dot sourcing to classes\Tesla.ps1
works fine, but this error is thrown from the Tesla file:
Unable to find type [Car]
If I import all the files in the correct order in MyScript.ps1, it works fine. Example:
. "$PSScriptRoot\classes\Car.ps1"
. "$PSScriptRoot\classes\Tesla.ps1"
$tesla = [Tesla]::new()
This is cumbersome, especially as the complexity grows. Am I dot sourcing incorrectly? Is there a better way to import a custom PowerShell class using a relative path that isn't in the PSModulePath?
The dot sourcing feature lets you run a script in the current scope instead of in the script scope. When you run a script that is dot sourced, the commands in the script run as though you had typed them at the command prompt.
Import Classes using Using Module Statement The assembly-importing feature is a convenience; the module-importing feature is a requirement, because there's no other way to import classes from script modules. The Using statement has a module parameter that takes a module name string or a ModuleSpecification object.
The PowerShell dot-source operator brings script files into the current session scope. It is a way to reuse script. All script functions and variables defined in the script file become part of the script it is dot sourced into. It is like copying and pasting text from the script file directly into your script.
PetSerAl, as countless times before, has provided the crucial pointers in a terse comment on the question:
Due to your use of classes, you must use modules and import them with using module
statements in order to use them.
Unfortunately, as of this writing, using module
is still not mentioned in Get-Help about_Modules
).
Specifically, in order to reference a type (class) in a class
definition, that type must be known to PowerShell at parse time.
In your example, in order to derive Tesla
from class Car
, type Car
must be known to PowerShell when the script is parsed, before execution begins - that's why it is too late to try to import Car
by dot-sourcing its containing script (. "$PSScriptRoot\Car.ps1"
)
PowerShell knows about referenced types at parse time in one of two ways:
If the type is already loaded into the current PowerShell session
Via a using module
statement that references a module in which the type (class) is defined (note that there's also a using assembly
statement for loading types from DLLs, and using namespace
to enable referring to types by their mere names).
Import-Module
cmdlet does not work in this case, because it executes at runtime.Therefore, as PetSerAl suggests:
Store your classes in module files (stand-alone *.psm1
files in the simplest case)
Then use using module
to have the Tesla
module import the Car
module and the MyScript.ps1
script import the Tesla
module, using paths that are relative to the enclosing script / module's location.
MyScript.ps1
classes\
Car.psm1 # Note the .psm1 extension
Tesla.psm1 # Ditto
Car.psm1
:
class Car {}
Tesla.psm1
:
using module .\Car.psm1
class Tesla : Car {}
MyScript.ps1
:
using module .\classes\Tesla.psm1
$tesla = [Tesla]::new()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With