Exporter des classes depuis des modules PowerShell
Ce serait pratique si les classes de modules étaient aussi faciles à consommer
que les fonctions de modules, non ? On s’attend naturellement à ce que
Import-Module expose tout ce dont on a besoin, mais la consommation des
classes a des contraintes supplémentaires au moment de l’analyse qui compliquent
la conception des modules.

Cet article utilise les mêmes exemples que le dépôt ModuleExportClasses.
Mais on ne peut pas exporter directement les classes
PowerShell ne permet pas d’exporter des classes avec Export-ModuleMember ni
via les clés du manifeste de module.

Cela mène généralement à l’une de ces situations :
- Obliger les consommateurs à ajouter
using moduleau moment de l’analyse - Rendre l’ordre des scripts fragile lorsque plusieurs modules dépendent de types de classes
- Rendre les conceptions basées sur des classes plus difficiles à découvrir et à réutiliser
L’instruction using module fonctionne, mais peut être fragile
Si un module définit une classe :
# .\MyModule\MyModule.psm1
class MyModuleClass {
static [string] DoStuff() {
return "I'm busy!"
}
}
vous pouvez la consommer avec :
# .\myScript.ps1
using module .\MyModule
[MyModuleClass]::DoStuff()
Le problème apparaît lorsqu’on chaîne des modules et dans l’usage interactif.

Si les instructions sont évaluées dans le même bloc d’analyse, cela peut fonctionner :

Introduction aux accélérateurs de type
Les accélérateurs de type sont des alias pour des types .NET. Nous pouvons nous
appuyer sur la classe interne
System.Management.Automation.TypeAccelerators pour enregistrer des alias vers
nos classes de module lors de l’import du module.
# Get the internal TypeAccelerators class
$typeAcceleratorsClass = [psobject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
)
$typeAcceleratorsClass::Get.GetEnumerator() | Select-Object -First 3

Exporter des alias de classes lors de l’import du module
Le modèle pratique consiste à ajouter la logique d’enregistrement à la fin du fichier du module, après la déclaration des classes :
$typesToExportWithNamespace = @(
'TheModuleClass'
)
$typeAcceleratorsClass = [psobject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
)
$moduleName = $MyInvocation.MyCommand.ScriptBlock.Module.Name
$existingTypeAccelerators = $typeAcceleratorsClass::Get
foreach ($typeToExport in $typesToExportWithNamespace) {
$fullTypeToExport = '{0}.{1}' -f $moduleName, $typeToExport
$type = $typeToExport -as [System.Type]
if (-not $type) {
throw "Type '$typeToExport' not found."
}
if ($fullTypeToExport -in $existingTypeAccelerators.Keys) {
Write-Warning "Overriding type accelerator '$fullTypeToExport'."
}
$null = $typeAcceleratorsClass::Add($fullTypeToExport, $type)
}
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
foreach ($typeName in $typesToExportWithNamespace) {
$null = $typeAcceleratorsClass::Remove('{0}.{1}' -f $moduleName, $typeName)
}
}.GetNewClosure()
Après l’import du module :

L’ordre de chargement reste important
Les accélérateurs de type ne sont enregistrés que lorsque le code d’import s’exécute. Si le module source ne peut pas être trouvé, ou s’il est importé trop tard, les appels échouent encore.

Après l’import du module source, le même chemin d’appel se résout :


La mise à jour de PSModulePath, ou l’installation des modules dans les
emplacements attendus, résout cela :
$psmodpath = $Env:PSModulePath -split [io.path]::PathSeparator
if ($psmodpath -notcontains $PWD.Path) { $psmodpath = @($PWD.Path) + $psmodpath }
$env:PSModulePath = $psmodpath -join [io.path]::PathSeparator

Le module dépendant et l’usage de la classe réussissent ensuite :

Contournement pour le nommage qualifié
Comme les classes PowerShell n’offrent pas ici l’ergonomie de véritables espaces
de noms, une convention de nommage pratique consiste à exporter les accélérateurs
sous la forme [ModuleName.ClassName]. Cela réduit les collisions tout en
gardant des sites d’appel lisibles.
Limites et compromis
- Pas de parité IntelliSense avec les espaces de noms de classes natifs.
- Une discipline de nommage des accélérateurs est nécessaire pour éviter les collisions.
- L’ordre d’import à l’exécution reste important pour les consommateurs.