À propos des classes
Description courte
Décrit comment utiliser des classes pour créer vos propres types personnalisés.
Description longue
PowerShell 5.0 ajoute une syntaxe formelle pour définir des classes et d’autres types définis par l’utilisateur. L’ajout de classes permet aux développeurs et aux professionnels de l’informatique d’adopter PowerShell pour un plus large éventail de cas d’usage. Il simplifie le développement d’artefacts PowerShell et accélère la couverture des surfaces de gestion.
Une déclaration de classe est un blueprint utilisé pour créer des instances d’objets au moment de l’exécution. Lorsque vous définissez une classe, le nom de la classe est le nom du type. Par exemple, si vous déclarez une classe nommée Device et initialisez une variable $dev
sur une nouvelle instance de Device, $dev
est un objet ou instance de type Device. Chaque instance d’appareil peut avoir des valeurs différentes dans ses propriétés.
Scénarios pris en charge
- Définissez des types personnalisés dans PowerShell à l’aide d’une sémantique de programmation orientée objet familière comme les classes, les propriétés, les méthodes, l’héritage, etc.
- Types de débogage à l’aide du langage PowerShell.
- Générez et gérez des exceptions à l’aide de mécanismes formels.
- Définissez les ressources DSC et leurs types associés à l’aide du langage PowerShell.
Syntax
Les classes sont déclarées à l’aide de la syntaxe suivante :
class <class-name> [: [<base-class>][,<interface-list]] {
[[<attribute>] [hidden] [static] <property-definition> ...]
[<class-name>([<constructor-argument-list>])
{<constructor-statement-list>} ...]
[[<attribute>] [hidden] [static] <method-definition> ...]
}
Les classes sont instanciées à l’aide de l’une des syntaxes suivantes :
[$<variable-name> =] New-Object -TypeName <class-name> [
[-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])
Notes
Lors de l’utilisation de la [<class-name>]::new(
syntaxe, les crochets autour du nom de classe sont obligatoires. Les crochets signalent une définition de type pour PowerShell.
Exemple de syntaxe et d’utilisation
Cet exemple montre la syntaxe minimale nécessaire pour créer une classe utilisable.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft
Propriétés de classe
Les propriétés sont des variables déclarées au niveau de l’étendue de la classe. Une propriété peut être de n’importe quel type intégré ou d’une instance d’une autre classe. Les classes n’ont aucune restriction quant au nombre de propriétés qu’elles ont.
Exemple de classe avec des propriétés simples
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
}
$device = [Device]::new()
$device.Brand = "Microsoft"
$device.Model = "Surface Pro 4"
$device.VendorSku = "5072641000"
$device
Brand Model VendorSku
----- ----- ---------
Microsoft Surface Pro 4 5072641000
Exemples de types complexes dans les propriétés de classe
Cet exemple définit une classe Rack vide à l’aide de la classe Device . Les exemples suivants montrent comment ajouter des appareils au rack et comment commencer par un rack préchargé.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
}
class Rack {
[string]$Brand
[string]$Model
[string]$VendorSku
[string]$AssetId
[Device[]]$Devices = [Device[]]::new(8)
}
$rack = [Rack]::new()
$rack
Brand :
Model :
VendorSku :
AssetId :
Devices : {$null, $null, $null, $null...}
Méthodes de classe
Les méthodes définissent les actions qu’une classe peut effectuer. Les méthodes peuvent prendre des paramètres qui fournissent des données d’entrée. Les méthodes peuvent retourner une sortie. Les données retournées par une méthode peuvent être n’importe quel type de données défini.
Exemple de classe simple avec des propriétés et des méthodes
Extension de la classe Rack pour y ajouter et supprimer des appareils.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
[string]ToString(){
return ("{0}|{1}|{2}" -f $this.Brand, $this.Model, $this.VendorSku)
}
}
class Rack {
[int]$Slots = 8
[string]$Brand
[string]$Model
[string]$VendorSku
[string]$AssetId
[Device[]]$Devices = [Device[]]::new($this.Slots)
[void] AddDevice([Device]$dev, [int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $dev
}
[void]RemoveDevice([int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $null
}
[int[]] GetAvailableSlots(){
[int]$i = 0
return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
}
}
$rack = [Rack]::new()
$surface = [Device]::new()
$surface.Brand = "Microsoft"
$surface.Model = "Surface Pro 4"
$surface.VendorSku = "5072641000"
$rack.AddDevice($surface, 2)
$rack
$rack.GetAvailableSlots()
Slots : 8
Brand :
Model :
VendorSku :
AssetId :
Devices : {$null, $null, Microsoft|Surface Pro 4|5072641000, $null...}
0
1
3
4
5
6
7
Sortie dans les méthodes de classe
Les méthodes doivent avoir un type de retour défini. Si une méthode ne retourne pas de sortie, le type de sortie doit être [void]
.
Dans les méthodes de classe, aucun objet n’est envoyé au pipeline à l’exception de ceux mentionnés dans l’instruction return
. Il n’y a pas de sortie accidentelle vers le pipeline à partir du code.
Notes
Cela est fondamentalement différent de la façon dont les fonctions PowerShell gèrent la sortie, où tout va au pipeline.
Sortie de la méthode
Cet exemple ne montre aucune sortie accidentelle vers le pipeline à partir de méthodes de classe, sauf sur l’instruction return
.
class FunWithIntegers
{
[int[]]$Integers = 0..10
[int[]]GetOddIntegers(){
return $this.Integers.Where({ ($_ % 2) })
}
[void] GetEvenIntegers(){
# this following line doesn't go to the pipeline
$this.Integers.Where({ ($_ % 2) -eq 0})
}
[string]SayHello(){
# this following line doesn't go to the pipeline
"Good Morning"
# this line goes to the pipeline
return "Hello World"
}
}
$ints = [FunWithIntegers]::new()
$ints.GetOddIntegers()
$ints.GetEvenIntegers()
$ints.SayHello()
1
3
5
7
9
Hello World
Constructeur
Les constructeurs vous permettent de définir des valeurs par défaut et de valider la logique d’objet au moment de la création de la instance de la classe. Les constructeurs ont le même nom que la classe . Les constructeurs peuvent avoir des arguments pour initialiser les membres de données du nouvel objet.
La classe peut avoir zéro ou plusieurs constructeurs définis. Si aucun constructeur n’est défini, la classe reçoit un constructeur sans paramètre par défaut. Ce constructeur initialise tous les membres à leurs valeurs par défaut. Les types d’objets et les chaînes reçoivent des valeurs null. Lorsque vous définissez un constructeur, aucun constructeur sans paramètre par défaut n’est créé. Create un constructeur sans paramètre si nécessaire.
Syntaxe de base du constructeur
Dans cet exemple, la classe Device est définie avec des propriétés et un constructeur. Pour utiliser cette classe, l’utilisateur doit fournir des valeurs pour les paramètres répertoriés dans le constructeur.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
Device(
[string]$b,
[string]$m,
[string]$vsk
){
$this.Brand = $b
$this.Model = $m
$this.VendorSku = $vsk
}
}
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")
$surface
Brand Model VendorSku
----- ----- ---------
Microsoft Surface Pro 4 5072641000
Exemple avec plusieurs constructeurs
Dans cet exemple, la classe Device est définie avec des propriétés, un constructeur par défaut et un constructeur pour initialiser le instance.
Le constructeur par défaut définit la marque sur Undefined et laisse model et vendor-sku avec des valeurs null.
class Device {
[string]$Brand
[string]$Model
[string]$VendorSku
Device(){
$this.Brand = 'Undefined'
}
Device(
[string]$b,
[string]$m,
[string]$vsk
){
$this.Brand = $b
$this.Model = $m
$this.VendorSku = $vsk
}
}
[Device]$somedevice = [Device]::new()
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")
$somedevice
$surface
Brand Model VendorSku
----- ----- ---------
Undefined
Microsoft Surface Pro 4 5072641000
Attribut masqué
L’attribut hidden
rend une propriété ou une méthode moins visible. La propriété ou la méthode est toujours accessible à l’utilisateur et est disponible dans toutes les étendues dans lesquelles l’objet est disponible. Les membres masqués sont masqués dans l’applet de commande et ne peuvent pas être affichés à l’aide Get-Member
de la saisie semi-automatique ou d’IntelliSense en dehors de la définition de classe.
Exemple utilisant des attributs masqués
Lorsqu’un objet Rack est créé, le nombre d’emplacements pour les appareils est une valeur fixe qui ne doit être modifiée à aucun moment. Cette valeur est connue au moment de la création.
L’utilisation de l’attribut masqué permet au développeur de garder le nombre d’emplacements masqués et d’éviter les modifications involontaires de la taille du rack.
class Device {
[string]$Brand
[string]$Model
}
class Rack {
[int] hidden $Slots = 8
[string]$Brand
[string]$Model
[Device[]]$Devices = [Device[]]::new($this.Slots)
Rack ([string]$b, [string]$m, [int]$capacity){
## argument validation here
$this.Brand = $b
$this.Model = $m
$this.Slots = $capacity
## reset rack size to new capacity
$this.Devices = [Device[]]::new($this.Slots)
}
}
[Rack]$r1 = [Rack]::new("Microsoft", "Surface Pro 4", 16)
$r1
$r1.Devices.Length
$r1.Slots
Brand Model Devices
----- ----- -------
Microsoft Surface Pro 4 {$null, $null, $null, $null...}
16
16
Notez que la propriété Slots n’est pas affichée dans la $r1
sortie. Toutefois, la taille a été modifiée par le constructeur.
Attribut statique
L’attribut static
définit une propriété ou une méthode qui existe dans la classe et qui n’a besoin d’aucun instance.
Une propriété statique est toujours disponible, indépendamment de l’instanciation de classe. Une propriété statique est partagée entre toutes les instances de la classe. Une méthode statique est toujours disponible. Toutes les propriétés statiques sont actives pour l’ensemble de l’étendue de session.
Exemple utilisant des attributs et des méthodes statiques
Supposons que les racks instanciés ici existent dans votre centre de données. Par conséquent, vous souhaitez effectuer le suivi des racks dans votre code.
class Device {
[string]$Brand
[string]$Model
}
class Rack {
hidden [int] $Slots = 8
static [Rack[]]$InstalledRacks = @()
[string]$Brand
[string]$Model
[string]$AssetId
[Device[]]$Devices = [Device[]]::new($this.Slots)
Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
## argument validation here
$this.Brand = $b
$this.Model = $m
$this.AssetId = $id
$this.Slots = $capacity
## reset rack size to new capacity
$this.Devices = [Device[]]::new($this.Slots)
## add rack to installed racks
[Rack]::InstalledRacks += $this
}
static [void]PowerOffRacks(){
foreach ($rack in [Rack]::InstalledRacks) {
Write-Warning ("Turning off rack: " + ($rack.AssetId))
}
}
}
Il existe une propriété et une méthode statiques de test
PS> [Rack]::InstalledRacks.Length
0
PS> [Rack]::PowerOffRacks()
PS> (1..10) | ForEach-Object {
>> [Rack]::new("Adatum Corporation", "Standard-16",
>> $_.ToString("Std0000"), 16)
>> } > $null
PS> [Rack]::InstalledRacks.Length
10
PS> [Rack]::InstalledRacks[3]
Brand Model AssetId Devices
----- ----- ------- -------
Adatum Corporation Standard-16 Std0004 {$null, $null, $null, $null...}
PS> [Rack]::PowerOffRacks()
WARNING: Turning off rack: Std0001
WARNING: Turning off rack: Std0002
WARNING: Turning off rack: Std0003
WARNING: Turning off rack: Std0004
WARNING: Turning off rack: Std0005
WARNING: Turning off rack: Std0006
WARNING: Turning off rack: Std0007
WARNING: Turning off rack: Std0008
WARNING: Turning off rack: Std0009
WARNING: Turning off rack: Std0010
Notez que le nombre de racks augmente chaque fois que vous exécutez cet exemple.
Attributs de validation de propriété
Les attributs de validation vous permettent de tester que les valeurs données aux propriétés répondent à des exigences définies. La validation est déclenchée au moment où la valeur est affectée. Voir about_functions_advanced_parameters.
Exemple utilisant des attributs de validation
class Device {
[ValidateNotNullOrEmpty()][string]$Brand
[ValidateNotNullOrEmpty()][string]$Model
}
[Device]$dev = [Device]::new()
Write-Output "Testing dev"
$dev
$dev.Brand = ""
Testing dev
Brand Model
----- -----
Exception setting "Brand": "The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again."
At C:\tmp\Untitled-5.ps1:11 char:1
+ $dev.Brand = ""
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], SetValueInvocationException
+ FullyQualifiedErrorId : ExceptionWhenSetting
Héritage dans les classes PowerShell
Vous pouvez étendre une classe en créant une classe qui dérive d’une classe existante. La classe dérivée hérite des propriétés de la classe de base. Vous pouvez ajouter ou remplacer des méthodes et des propriétés en fonction des besoins.
PowerShell ne prend pas en charge plusieurs héritages. Les classes ne peuvent pas hériter de plusieurs classes. Toutefois, vous pouvez utiliser des interfaces à cet effet.
L’implémentation d’héritage est définie par l’opérateur :
, ce qui signifie étendre cette classe ou implémente ces interfaces. La classe dérivée doit toujours être la plus à gauche dans la déclaration de classe.
Exemple utilisant la syntaxe d’héritage simple
Cet exemple montre la syntaxe d’héritage de classe PowerShell simple.
Class Derived : Base {...}
Cet exemple montre l’héritage avec une déclaration d’interface venant après la classe de base.
Class Derived : Base.Interface {...}
Exemple d’héritage simple dans les classes PowerShell
Dans cet exemple, les classes Rack et Device utilisées dans les exemples précédents sont mieux définies pour éviter les répétitions de propriétés, mieux aligner les propriétés communes et réutiliser la logique métier commune.
La plupart des objets du centre de données sont des ressources d’entreprise, ce qui est judicieux de commencer à les suivre en tant que ressources. Les types d’appareils sont définis par l’énumération DeviceType
. Consultez about_Enum pour plus d’informations sur les énumérations.
Dans notre exemple, nous définissons Rack
uniquement et ComputeServer
; les deux extensions de la Device
classe .
enum DeviceType {
Undefined = 0
Compute = 1
Storage = 2
Networking = 4
Communications = 8
Power = 16
Rack = 32
}
class Asset {
[string]$Brand
[string]$Model
}
class Device : Asset {
hidden [DeviceType]$devtype = [DeviceType]::Undefined
[string]$Status
[DeviceType] GetDeviceType(){
return $this.devtype
}
}
class ComputeServer : Device {
hidden [DeviceType]$devtype = [DeviceType]::Compute
[string]$ProcessorIdentifier
[string]$Hostname
}
class Rack : Device {
hidden [DeviceType]$devtype = [DeviceType]::Rack
hidden [int]$Slots = 8
[string]$Datacenter
[string]$Location
[Device[]]$Devices = [Device[]]::new($this.Slots)
Rack (){
## Just create the default rack with 8 slots
}
Rack ([int]$s){
## Add argument validation logic here
$this.Devices = [Device[]]::new($s)
}
[void] AddDevice([Device]$dev, [int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $dev
}
[void] RemoveDevice([int]$slot){
## Add argument validation logic here
$this.Devices[$slot] = $null
}
}
$FirstRack = [Rack]::new(16)
$FirstRack.Status = "Operational"
$FirstRack.Datacenter = "PNW"
$FirstRack.Location = "F03R02.J10"
(0..15).ForEach({
$ComputeServer = [ComputeServer]::new()
$ComputeServer.Brand = "Fabrikam, Inc." ## Inherited from Asset
$ComputeServer.Model = "Fbk5040" ## Inherited from Asset
$ComputeServer.Status = "Installed" ## Inherited from Device
$ComputeServer.ProcessorIdentifier = "x64" ## ComputeServer
$ComputeServer.Hostname = ("r1s" + $_.ToString("000")) ## ComputeServer
$FirstRack.AddDevice($ComputeServer, $_)
})
$FirstRack
$FirstRack.Devices
Datacenter : PNW
Location : F03R02.J10
Devices : {r1s000, r1s001, r1s002, r1s003...}
Status : Operational
Brand :
Model :
ProcessorIdentifier : x64
Hostname : r1s000
Status : Installed
Brand : Fabrikam, Inc.
Model : Fbk5040
ProcessorIdentifier : x64
Hostname : r1s001
Status : Installed
Brand : Fabrikam, Inc.
Model : Fbk5040
<... content truncated here for brevity ...>
ProcessorIdentifier : x64
Hostname : r1s015
Status : Installed
Brand : Fabrikam, Inc.
Model : Fbk5040
Appel des constructeurs de classe de base
Pour appeler un constructeur de classe de base à partir d’une sous-classe, ajoutez le base
mot clé.
class Person {
[int]$Age
Person([int]$a)
{
$this.Age = $a
}
}
class Child : Person
{
[string]$School
Child([int]$a, [string]$s ) : base($a) {
$this.School = $s
}
}
[Child]$littleone = [Child]::new(10, "Silver Fir Elementary School")
$littleone.Age
10
Appeler des méthodes de classe de base
Pour remplacer les méthodes existantes dans les sous-classes, déclarez des méthodes à l’aide du même nom et de la même signature.
class BaseClass
{
[int]days() {return 1}
}
class ChildClass1 : BaseClass
{
[int]days () {return 2}
}
[ChildClass1]::new().days()
2
Pour appeler des méthodes de classe de base à partir d’implémentations remplacées, effectuez un cast en classe de base ([baseclass]$this) lors de l’appel.
class BaseClass
{
[int]days() {return 1}
}
class ChildClass1 : BaseClass
{
[int]days () {return 2}
[int]basedays() {return ([BaseClass]$this).days()}
}
[ChildClass1]::new().days()
[ChildClass1]::new().basedays()
2
1
Interfaces
La syntaxe de déclaration d’interfaces est similaire à C#. Vous pouvez déclarer des interfaces après les types de base ou immédiatement après un signe deux-points (:
) lorsqu’aucun type de base n’est spécifié. Séparez tous les noms de type par des virgules.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableBar : bar, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Importation de classes à partir d’un module PowerShell
Import-Module
et l’instruction #requires
importe uniquement les fonctions de module, les alias et les variables, telles que définies par le module. Les classes ne sont pas importées. L’instruction using module
importe les classes définies dans le module. Si le module n’est pas chargé dans la session active, l’instruction using
échoue.