J'ai une configuration de module qui ressemble à une bibliothèque pour quelques autres scripts. Je n'arrive pas à comprendre comment obtenir une déclaration de classe dans la portée du script en appelant Import-Module
. J'ai essayé d'organiser Export-Module
avec un argument -class
, comme le -function
, mais il n'y a pas de -class
disponible. Dois-je simplement déclarer la classe dans chaque script?
La mise en place:
import-module holidays
Voici à quoi ressemble la classe:
Class data_block
{
$array
$rows
$cols
data_block($a,$r,$c)
{
$this.array = $a
$this.rows = $r
$this.cols = $c
}
}
J'ai trouvé un moyen de charger les classes sans avoir besoin d'utiliser "module". Dans votre fichier MyModule.psd1, utilisez la ligne:
ScriptsToProcess = @('Class.ps1')
Et puis mettez vos classes dans le fichier Class.ps1:
class MyClass {}
Mise à jour: Bien que vous n'ayez pas à utiliser "using module MyModule" avec cette méthode, vous devez toujours:
using
est sujet aux piègesLe mot clé using
est sujet aux pièges suivants:
using
ne fonctionne pas pour les modules qui ne sont pas dans PSModulePath
sauf si vous spécifiez le chemin complet du module dans l'instruction using
. Ceci est plutôt surprenant car bien qu’un module soit disponible via Get-Module
, l’instruction using
peut ne pas fonctionner en fonction du mode de chargement du module.using
ne peut être utilisée qu'au tout début d'un "script". Aucune combinaison de [scriptblock]::Create()
ou New-Module
ne semble pouvoir être surmontée. Une chaîne passée à Invoke-Expression
semble agir comme une sorte de script autonome; une instruction using
au début d'une telle chaîne de travaux. C'est-à-dire que Invoke-Expression "using module $path"
peut réussir, mais la portée dans laquelle le contenu du module est mis à disposition semble plutôt impénétrable. Par exemple, si Invoke-Expression "using module $path"
est utilisé dans un scriptblock Pester, les classes du module ne sont pas disponibles à partir du même scriptblock Pester.Les déclarations ci-dessus sont basées sur cet ensemble de tests .
.NewBoundScriptBlock()
semble fonctionner de manière fiableL'appel d'un scriptblock lié au module contenant la classe semble fonctionner de manière fiable pour exporter des instances d'une classe et ne souffre pas des pièges que using
fait. Considérons ce module qui contient une classe et a été importé:
New-Module 'ModuleName' { class c {$p = 'some value'} } |
Import-Module
Invoquer [c]::new()
dans un scriptblock lié au module produit un objet de type [c]
:
PS C:\> $c = & (Get-Module 'ModuleName').NewBoundScriptBlock({[c]::new()})
PS C:\> $c.p
some value
J'ai également rencontré plusieurs problèmes concernant les classes PowerShell dans la v5.
J'ai décidé d'utiliser la solution suivante pour le moment, car elle est parfaitement compatible avec .net et PowerShell:
Add-Type -Language CSharp -TypeDefinition @"
namespace My.Custom.Namespace {
public class Example
{
public string Name { get; set; }
public System.Management.Automation.PSCredential Credential { get; set; }
// ...
}
}
"@
L'avantage est que vous n'avez pas besoin d'un assembly personnalisé pour ajouter une définition de type, vous pouvez ajouter la définition de classe en ligne dans vos scripts ou modules PowerShell.
Le seul inconvénient est que vous devrez créer un nouveau runtime pour recharger la définition de classe une fois qu'il a été chargé pour la première fois (comme pour le chargement d'assemblys dans un domaine c #/.net).
Vous ne pouvez pas beaucoup. Selon about_Classes
help:
Mot clé de classe
Définit une nouvelle classe. C'est un vrai type .NET Framework. Les membres de classe sont publics, mais uniquement dans la portée du module. Vous ne pouvez pas faire référence au nom du type en tant que chaîne (par exemple, New-Object ne fonctionne pas), et dans cette version, vous ne pouvez pas utiliser de type littéral (par exemple, [MyClass]) en dehors du script/fichier de module dans lequel la classe est définie.
Cela signifie que si vous souhaitez obtenir vous-même une instance data_block
ou utiliser des fonctions qui exploitent ces classes, créez une fonction, par exemple, New-DataBlock
et faites-lui renvoyer une nouvelle instance data_block
, que vous pourrez ensuite utiliser pour obtenir des méthodes et des propriétés de classe ceux statiques).
Cela ne fonctionne certainement pas comme prévu.
L’idée de PS 5 est que vous pouvez définir votre classe dans un fichier séparé portant l’extension .psm1.
Ensuite, vous pouvez charger la définition avec la commande (par exemple):
using module C:\classes\whatever\path\to\file.psm1
Ce doit être la première ligne de votre script (après les commentaires).
Ce qui cause tant de peine, c'est que même si les définitions de classe sont appelées à partir d'un script, les modules sont chargés pour toute la session. Vous pouvez le voir en lançant:
get-module
Vous verrez le nom du fichier que vous avez chargé. Peu importe si vous exécutez le script à nouveau, ilNOTne rechargera pas les définitions de classe! (Ne lira même pas le fichier psm1.) Cela provoque beaucoup de grincements de dents.
Parfois - parfois - vous pouvez exécuter cette commande avant d'exécuter le script, ce qui rechargera le module avec les définitions de classe actualisées:
remove-module file
où fichier est le nom sans chemin ni extension. Cependant, pour préserver votre santé, je vous recommande de redémarrer la session PS. C'est évidemment encombrant; Microsoft doit en quelque sorte nettoyer cela.
Pour mettre à jour les définitions de classe en cours de développement, sélectionnez le code de la classe et appuyez sur F8
pour exécuter le code sélectionné. Pas aussi propre que l'option -Force
de la commande Import-Module
. Voir comme utiliser Module
n'a pas cette option et Remove-Module
est au mieux sporadique, c'est le meilleur moyen que j'ai trouvé pour développer une classe et voir les résultats sans avoir à fermer ISE et à le redémarrer.
La déclaration using est la voie à suivre si cela fonctionne pour vous. sinon, cela semble fonctionner aussi.
testclass.psm1
Utiliser une fonction pour donner la classe
class abc{
$testprop = 'It Worked!'
[int]testMethod($num){return $num * 5}
}
function abc(){
return [abc]::new()
}
Export-ModuleMember -Function abc
someScript.ps1
Import-Module path\to\testclass.psm1
$testclass = abc
$testclass.testProp # returns 'It Worked!'
$testclass.testMethod(500) # returns 2500
$testclass | gm
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
testMethod Method int testMethod(System.Object num)
ToString Method string ToString()
testprop Property System.Object testprop {get;set;}
Pour résoudre ce problème, j'ai déplacé votre définition de classe personnalisée dans un fichier .ps1 vide portant le même nom (comme dans Java/C #), puis chargé à la fois dans la définition du module et dans votre code dépendant. sourcing de points. Je sais que ce n'est pas génial, mais pour moi, c'est mieux que de devoir gérer plusieurs définitions de la même classe dans plusieurs fichiers ...
J'ai essayé toutes les approches de cette page et j’ai ENCORE cherché une solution qui fonctionne à plusieurs reprises. Je trouve que la mise en cache des modules est l'aspect le plus frustrant de cette opération ... Je peux importer des classes en tant que modules et même faire fonctionner le code, mais powershell ne reconnaît que la nouvelle classe par intermittence ... c'est presque comme si Powershell vous en avait jamais ajouté une .. .