Condividi tramite


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.

Vedi anche