Informazioni sulle classi
Breve descrizione
Descrive come è possibile usare le classi per creare tipi personalizzati.
Descrizione lunga
PowerShell 5.0 aggiunge una sintassi formale per definire classi e altri tipi definiti dall'utente. L'aggiunta di classi consente agli sviluppatori e ai professionisti IT di adottare PowerShell per un'ampia gamma di casi d'uso. Semplifica lo sviluppo di artefatti di PowerShell e accelera la copertura delle superfici di gestione.
Una dichiarazione di classe è un progetto usato per creare istanze di oggetti in fase di esecuzione. Quando si definisce una classe, il nome della classe è il nome del tipo. Ad esempio, se si dichiara una classe denominata Device e si inizializza una variabile $dev
in una nuova istanza di Device, $dev
è un oggetto o un'istanza di tipo Device. Ogni istanza di Device può avere valori diversi nelle relative proprietà.
Scenari supportati
- Definire tipi personalizzati in PowerShell usando semantica di programmazione orientata agli oggetti familiari, ad esempio classi, proprietà, metodi, ereditarietà e così via.
- Eseguire il debug dei tipi usando il linguaggio di PowerShell.
- Generare e gestire le eccezioni usando meccanismi formali.
- Definire le risorse DSC e i relativi tipi associati usando il linguaggio di PowerShell.
Sintassi
Le classi vengono dichiarate usando la sintassi seguente:
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> ...]
}
Le classi vengono create per creare un'istanza usando una delle sintassi seguenti:
[$<variable-name> =] New-Object -TypeName <class-name> [
[-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])
Nota
Quando si usa la [<class-name>]::new(
sintassi, le parentesi quadre intorno al nome della classe sono obbligatorie. Le parentesi quadre segnalano una definizione di tipo per PowerShell.
Sintassi e utilizzo di esempio
In questo esempio viene illustrata la sintassi minima necessaria per creare una classe utilizzabile.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft
Proprietà della classe
Le proprietà sono variabili dichiarate nell'ambito della classe. Una proprietà può essere di qualsiasi tipo predefinito o di un'istanza di un'altra classe. Le classi non hanno alcuna restrizione nel numero di proprietà che hanno.
Classe di esempio con proprietà semplici
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
Esempi di tipi complessi nelle proprietà della classe
In questo esempio viene definita una classe Rack vuota usando la classe Device . Gli esempi, seguendo questo esempio, mostrano come aggiungere dispositivi al rack e come iniziare con un rack precaricati.
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...}
Metodi di classe
I metodi definiscono le azioni che una classe è in grado di eseguire. I metodi possono accettare parametri che forniscono dati di input. I metodi possono restituire l'output. I dati restituiti da un metodo possono essere qualsiasi tipo di dati definito.
Classe semplice di esempio con proprietà e metodi
Estensione della classe Rack per aggiungere e rimuovere dispositivi da o verso di esso.
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
Output nei metodi della classe
I metodi devono avere un tipo restituito definito. Se un metodo non restituisce l'output, il tipo di output deve essere [void]
.
Nei metodi di classe non vengono inviati oggetti alla pipeline, ad eccezione di quelli indicati nell'istruzione return
. Non è presente alcun output accidentale nella pipeline dal codice.
Nota
Questo è fondamentalmente diverso dal modo in cui Le funzioni di PowerShell gestiscono l'output, dove tutto passa alla pipeline.
Output del metodo
In questo esempio non viene illustrato alcun output accidentale alla pipeline dai metodi della classe, ad eccezione dell'istruzione 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
Costruttore
I costruttori consentono di impostare valori predefiniti e convalidare la logica dell'oggetto al momento della creazione dell'istanza della classe. I costruttori hanno lo stesso nome della classe. I costruttori potrebbero avere argomenti per inizializzare i membri dei dati del nuovo oggetto.
La classe può avere zero o più costruttori definiti. Se non viene definito alcun costruttore, la classe viene assegnata a un costruttore senza parametri predefinito. Questo costruttore inizializza tutti i membri ai valori predefiniti. I tipi di oggetto e le stringhe sono dati valori Null. Quando si definisce il costruttore, non viene creato alcun costruttore senza parametri predefinito. Create un costruttore senza parametri se necessario.
Sintassi di base del costruttore
In questo esempio la classe Device viene definita con proprietà e un costruttore. Per usare questa classe, è necessario che l'utente fornisca valori per i parametri elencati nel costruttore.
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
Esempio con più costruttori
In questo esempio la classe Device viene definita con proprietà, un costruttore predefinito e un costruttore per inizializzare l'istanza.
Il costruttore predefinito imposta il marchio su Undefined e lascia modello e sku fornitore con valori 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
Attributo nascosto
L'attributo hidden
rende meno visibile una proprietà o un metodo. La proprietà o il metodo è ancora accessibile all'utente ed è disponibile in tutti gli ambiti in cui è disponibile l'oggetto. I membri nascosti sono nascosti dal cmdlet e non possono essere visualizzati usando il Get-Member
completamento della scheda o IntelliSense all'esterno della definizione della classe.
Esempio di uso di attributi nascosti
Quando viene creato un oggetto Rack , il numero di slot per i dispositivi è un valore fisso che non deve essere modificato in qualsiasi momento. Questo valore è noto in fase di creazione.
L'uso dell'attributo nascosto consente allo sviluppatore di mantenere nascosto il numero di slot e impedisce modifiche involontarie delle dimensioni del 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
La proprietà Slot di avviso non viene visualizzata nell'output $r1
. Tuttavia, le dimensioni sono state modificate dal costruttore.
Attributo statico
L'attributo static
definisce una proprietà o un metodo presente nella classe e non necessita di alcuna istanza.
Una proprietà statica è sempre disponibile, indipendente dall'istanza della classe. Una proprietà statica viene condivisa in tutte le istanze della classe. Un metodo statico è sempre disponibile. Tutte le proprietà statiche sono attive per l'intero intervallo di sessione.
Esempio di uso di attributi e metodi statici
Si supponga che le rack create qui esistano nel data center. Quindi, si vuole tenere traccia dei rack nel codice.
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))
}
}
}
Testare la proprietà statica e il metodo esistono
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
Si noti che il numero di rack aumenta ogni volta che si esegue questo esempio.
Attributi di convalida delle proprietà
Gli attributi di convalida consentono di testare i valori assegnati alle proprietà che soddisfano i requisiti definiti. La convalida viene attivata al momento dell'assegnazione del valore. Vedere about_functions_advanced_parameters.
Esempio di uso degli attributi di convalida
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
Ereditarietà nelle classi di PowerShell
È possibile estendere una classe creando una nuova classe che deriva da una classe esistente. La classe derivata eredita le proprietà della classe base. È possibile aggiungere o eseguire l'override di metodi e proprietà in base alle esigenze.
PowerShell non supporta più ereditarietà. Le classi non possono ereditare da più classi. Tuttavia, è possibile usare le interfacce per tale scopo.
L'implementazione dell'ereditarietà è definita dall'operatore :
. Ciò significa estendere questa classe o implementare queste interfacce. La classe derivata deve essere sempre all'estrema sinistra nella dichiarazione di classe.
Esempio di uso della sintassi di ereditarietà semplice
In questo esempio viene illustrata la sintassi di ereditarietà semplice della classe Di PowerShell.
Class Derived : Base {...}
In questo esempio viene illustrata l'ereditarietà con una dichiarazione di interfaccia successiva alla classe di base.
Class Derived : Base.Interface {...}
Esempio di ereditarietà semplice nelle classi di PowerShell
In questo esempio le classi Rack e Device usate negli esempi precedenti sono definite meglio per evitare ripetizioni delle proprietà, allineare meglio le proprietà comuni e riutilizzare la logica di business comune.
La maggior parte degli oggetti nel data center è costituita da asset aziendali, il che ha senso iniziare a monitorarli come asset. I tipi di dispositivo sono definiti dall'enumerazione DeviceType
, vedere about_Enum per informazioni dettagliate sulle enumerazioni.
Nell'esempio viene definito Rack
solo e ComputeServer
, entrambe le estensioni alla 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
Chiamata di costruttori di classi di base
Per richiamare un costruttore della classe base da una sottoclasse, aggiungere la base
parola chiave .
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
Richiamare i metodi della classe base
Per eseguire l'override dei metodi esistenti nelle sottoclassi, dichiarare i metodi usando lo stesso nome e la stessa firma.
class BaseClass
{
[int]days() {return 1}
}
class ChildClass1 : BaseClass
{
[int]days () {return 2}
}
[ChildClass1]::new().days()
2
Per chiamare i metodi della classe base da implementazioni sottoposte a override, eseguire il cast alla classe base ([baseclass]$this) alla chiamata.
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
Interfacce
La sintassi per la dichiarazione delle interfacce è simile a C#. È possibile dichiarare le interfacce dopo i tipi di base o immediatamente dopo i due punti (:
) quando non è specificato alcun tipo di base. Separare tutti i nomi dei tipi con virgole.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableBar : bar, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Importazione di classi da un modulo di PowerShell
Import-Module
e l'istruzione #requires
importa solo le funzioni, gli alias e le variabili del modulo, come definito dal modulo. Le classi non vengono importate. L'istruzione using module
importa le classi definite nel modulo. Se il modulo non viene caricato nella sessione corrente, l'istruzione using
ha esito negativo.