about_Classes
Descrizione breve
Viene descritto come usare le classi per creare tipi personalizzati.
Descrizione lunga
A partire dalla versione 5.0, PowerShell ha 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.
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 dichiari una classe denominata Device e inizializzi 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, ad esempio classi, proprietà, metodi, ereditarietà e così via.
- Definire le risorse DSC e i relativi tipi associati usando il linguaggio di PowerShell.
- Definire attributi personalizzati per decorare variabili, parametri e definizioni di tipi personalizzati.
- Definire eccezioni personalizzate che possono essere rilevate dal nome del tipo.
Sintassi
Sintassi della definizione
Le definizioni di classe usano 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> ...]
}
Sintassi di creazione di istanze
Per creare un'istanza di una classe, usare una delle sintassi seguenti:
[$<variable-name> =] New-Object -TypeName <class-name> [
[-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])
[$<variable-name> =] [<class-name>]@{[<class-property-hashtable>]}
Nota
Quando si usa la [<class-name>]::new()
sintassi, le parentesi attorno al nome della classe sono obbligatorie. Le parentesi quadre indicano una definizione di tipo per PowerShell.
La sintassi della tabella hash funziona solo per le classi con un costruttore predefinito che non prevede parametri. Crea un'istanza della classe con il costruttore predefinito e quindi assegna le coppie chiave-valore alle proprietà dell'istanza. Se una chiave nella tabella hash non è un nome di proprietà valido, PowerShell genera un errore.
Esempi
Esempio 1 - Definizione minima
Questo esempio mostra la sintassi minima necessaria per creare una classe utilizzabile.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Fabrikam, Inc."
$dev
Brand
-----
Fabrikam, Inc.
Esempio 2 - Classe con membri dell'istanza
In questo esempio viene definita una classe Book con diverse proprietà, costruttori e metodi. Ogni membro definito è un membro di istanza , non un membro statico. È possibile accedere alle proprietà e ai metodi solo tramite un'istanza creata della classe .
class Book {
# Class properties
[string] $Title
[string] $Author
[string] $Synopsis
[string] $Publisher
[datetime] $PublishDate
[int] $PageCount
[string[]] $Tags
# Default constructor
Book() { $this.Init(@{}) }
# Convenience constructor from hashtable
Book([hashtable]$Properties) { $this.Init($Properties) }
# Common constructor for title and author
Book([string]$Title, [string]$Author) {
$this.Init(@{Title = $Title; Author = $Author })
}
# Shared initializer method
[void] Init([hashtable]$Properties) {
foreach ($Property in $Properties.Keys) {
$this.$Property = $Properties.$Property
}
}
# Method to calculate reading time as 2 minutes per page
[timespan] GetReadingTime() {
if ($this.PageCount -le 0) {
throw 'Unable to determine reading time from page count.'
}
$Minutes = $this.PageCount * 2
return [timespan]::new(0, $Minutes, 0)
}
# Method to calculate how long ago a book was published
[timespan] GetPublishedAge() {
if (
$null -eq $this.PublishDate -or
$this.PublishDate -eq [datetime]::MinValue
) { throw 'PublishDate not defined' }
return (Get-Date) - $this.PublishDate
}
# Method to return a string representation of the book
[string] ToString() {
return "$($this.Title) by $($this.Author) ($($this.PublishDate.Year))"
}
}
Il frammento di codice seguente crea un'istanza della classe e mostra come si comporta. Dopo aver creato un'istanza della classe Book , nell'esempio vengono utilizzati i GetReadingTime()
metodi e GetPublishedAge()
per scrivere un messaggio relativo al libro.
$Book = [Book]::new(@{
Title = 'The Hobbit'
Author = 'J.R.R. Tolkien'
Publisher = 'George Allen & Unwin'
PublishDate = '1937-09-21'
PageCount = 310
Tags = @('Fantasy', 'Adventure')
})
$Book
$Time = $Book.GetReadingTime()
$Time = @($Time.Hours, 'hours and', $Time.Minutes, 'minutes') -join ' '
$Age = [Math]::Floor($Book.GetPublishedAge().TotalDays / 365.25)
"It takes $Time to read $Book,`nwhich was published $Age years ago."
Title : The Hobbit
Author : J.R.R. Tolkien
Synopsis :
Publisher : George Allen & Unwin
PublishDate : 9/21/1937 12:00:00 AM
PageCount : 310
Tags : {Fantasy, Adventure}
It takes 10 hours and 20 minutes to read The Hobbit by J.R.R. Tolkien (1937),
which was published 86 years ago.
Esempio 3 - Classe con membri statici
La classe BookList in questo esempio si basa sulla classe Book nell'esempio 2. Anche se la classe BookList non può essere contrassegnata come statica, l'implementazione definisce solo la proprietà statica Books e un set di metodi statici per la gestione di tale proprietà.
class BookList {
# Static property to hold the list of books
static [System.Collections.Generic.List[Book]] $Books
# Static method to initialize the list of books. Called in the other
# static methods to avoid needing to explicit initialize the value.
static [void] Initialize() { [BookList]::Initialize($false) }
static [bool] Initialize([bool]$force) {
if ([BookList]::Books.Count -gt 0 -and -not $force) {
return $false
}
[BookList]::Books = [System.Collections.Generic.List[Book]]::new()
return $true
}
# Ensure a book is valid for the list.
static [void] Validate([book]$Book) {
$Prefix = @(
'Book validation failed: Book must be defined with the Title,'
'Author, and PublishDate properties, but'
) -join ' '
if ($null -eq $Book) { throw "$Prefix was null" }
if ([string]::IsNullOrEmpty($Book.Title)) {
throw "$Prefix Title wasn't defined"
}
if ([string]::IsNullOrEmpty($Book.Author)) {
throw "$Prefix Author wasn't defined"
}
if ([datetime]::MinValue -eq $Book.PublishDate) {
throw "$Prefix PublishDate wasn't defined"
}
}
# Static methods to manage the list of books.
# Add a book if it's not already in the list.
static [void] Add([Book]$Book) {
[BookList]::Initialize()
[BookList]::Validate($Book)
if ([BookList]::Books.Contains($Book)) {
throw "Book '$Book' already in list"
}
$FindPredicate = {
param([Book]$b)
$b.Title -eq $Book.Title -and
$b.Author -eq $Book.Author -and
$b.PublishDate -eq $Book.PublishDate
}.GetNewClosure()
if ([BookList]::Books.Find($FindPredicate)) {
throw "Book '$Book' already in list"
}
[BookList]::Books.Add($Book)
}
# Clear the list of books.
static [void] Clear() {
[BookList]::Initialize()
[BookList]::Books.Clear()
}
# Find a specific book using a filtering scriptblock.
static [Book] Find([scriptblock]$Predicate) {
[BookList]::Initialize()
return [BookList]::Books.Find($Predicate)
}
# Find every book matching the filtering scriptblock.
static [Book[]] FindAll([scriptblock]$Predicate) {
[BookList]::Initialize()
return [BookList]::Books.FindAll($Predicate)
}
# Remove a specific book.
static [void] Remove([Book]$Book) {
[BookList]::Initialize()
[BookList]::Books.Remove($Book)
}
# Remove a book by property value.
static [void] RemoveBy([string]$Property, [string]$Value) {
[BookList]::Initialize()
$Index = [BookList]::Books.FindIndex({
param($b)
$b.$Property -eq $Value
}.GetNewClosure())
if ($Index -ge 0) {
[BookList]::Books.RemoveAt($Index)
}
}
}
Ora che BookList è definito, il libro dell'esempio precedente può essere aggiunto all'elenco.
$null -eq [BookList]::Books
[BookList]::Add($Book)
[BookList]::Books
True
Title : The Hobbit
Author : J.R.R. Tolkien
Synopsis :
Publisher : George Allen & Unwin
PublishDate : 9/21/1937 12:00:00 AM
PageCount : 310
Tags : {Fantasy, Adventure}
Il frammento di codice seguente chiama i metodi statici per la classe .
[BookList]::Add([Book]::new(@{
Title = 'The Fellowship of the Ring'
Author = 'J.R.R. Tolkien'
Publisher = 'George Allen & Unwin'
PublishDate = '1954-07-29'
PageCount = 423
Tags = @('Fantasy', 'Adventure')
}))
[BookList]::Find({
param ($b)
$b.PublishDate -gt '1950-01-01'
}).Title
[BookList]::FindAll({
param($b)
$b.Author -match 'Tolkien'
}).Title
[BookList]::Remove($Book)
[BookList]::Books.Title
[BookList]::RemoveBy('Author', 'J.R.R. Tolkien')
"Titles: $([BookList]::Books.Title)"
[BookList]::Add($Book)
[BookList]::Add($Book)
The Fellowship of the Ring
The Hobbit
The Fellowship of the Ring
The Fellowship of the Ring
Titles:
Book 'The Hobbit by J.R.R. Tolkien (1937)' already in list
At C:\code\classes.examples.ps1:114 char:13
+ throw "Book '$Book' already in list"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (Book 'The Hobbi...alread
y in list:String) [], RuntimeException
+ FullyQualifiedErrorId : Book 'The Hobbit by J.R.R. Tolkien (1937)'
already in list
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 possono avere zero o più proprietà. Le classi non hanno un numero massimo di proprietà.
Per altre informazioni, vedere about_Classes_Properties.
Metodi di classe
I metodi definiscono le azioni che una classe è in grado di eseguire. I metodi possono accettare parametri che specificano i dati di input. I metodi definiscono sempre un tipo di output. Se un metodo non restituisce alcun output, deve avere il tipo di output Void . Se un metodo non definisce in modo esplicito un tipo di output, il tipo di output del metodo è Void.
Per altre informazioni, vedere about_Classes_Methods.
Costruttori di classi
I costruttori consentono di impostare i 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 possono avere parametri per inizializzare i membri dati del nuovo oggetto.
Per altre informazioni, vedere about_Classes_Constructors.
Parola chiave Hidden
La hidden
parola chiave nasconde un membro della classe. Il membro è ancora accessibile all'utente ed è disponibile in tutti gli ambiti in cui l'oggetto è disponibile.
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.
La hidden
parola chiave si applica solo ai membri della classe, non a una classe stessa.
I membri della classe nascosti sono:
- Non incluso nell'output predefinito per la classe .
- Non incluso nell'elenco dei membri della classe restituiti dal
Get-Member
cmdlet . Per visualizzare i membri nascosti conGet-Member
, usare il parametro Force . - Non visualizzato nel completamento della scheda o IntelliSense, a meno che il completamento non si verifichi nella classe che definisce il membro nascosto.
- Membri pubblici della classe . È possibile accedervi, ereditare e modificare. Nascondere un membro non lo rende privato. Nasconde solo il membro come descritto nei punti precedenti.
Nota
Quando si nasconde qualsiasi overload per un metodo, tale metodo viene rimosso da IntelliSense, i risultati di completamento e l'output predefinito per Get-Member
.
Quando si nasconde un costruttore, l'opzione new()
viene rimossa da IntelliSense e dai risultati di completamento.
Per altre informazioni sulla parola chiave, vedere about_Hidden. Per altre informazioni sulle proprietà nascoste, vedere about_Classes_Properties. Per altre informazioni sui metodi nascosti, vedere about_Classes_Methods. Per altre informazioni sui costruttori nascosti, vedere about_Classes_Constructors.
Parola chiave static
La static
parola chiave definisce una proprietà o un metodo presente nella classe e non richiede alcuna istanza.
Una proprietà statica è sempre disponibile, indipendente dalla creazione di istanze 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.
La static
parola chiave si applica solo ai membri della classe, non a una classe stessa.
Per altre informazioni sulle proprietà statiche, vedere about_Classes_Properties. Per altre informazioni sui metodi statici, vedere about_Classes_Methods. Per altre informazioni sui costruttori statici, vedere about_Classes_Constructors.
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à e i metodi della classe base. È possibile aggiungere o eseguire l'override dei membri della classe base in base alle esigenze.
PowerShell non supporta l'ereditarietà multipla. Le classi non possono ereditare direttamente da più classi.
Le classi possono anche ereditare da interfacce, che definiscono un contratto. Una classe che eredita da un'interfaccia deve implementare tale contratto. In tal caso, la classe può essere usata come qualsiasi altra classe che implementa tale interfaccia.
Per altre informazioni sulla derivazione di classi che ereditano da una classe di base o implementano interfacce, vedere about_Classes_Inheritance.
Esportazione di classi con acceleratori di tipo
Per impostazione predefinita, i moduli di PowerShell non esportano automaticamente classi ed enumerazioni definite in PowerShell. I tipi personalizzati non sono disponibili all'esterno del modulo senza chiamare un'istruzione using module
.
Tuttavia, se un modulo aggiunge acceleratori di tipo, tali acceleratori di tipo sono immediatamente disponibili nella sessione dopo l'importazione del modulo da parte degli utenti.
Nota
L'aggiunta di acceleratori di tipi alla sessione usa un'API interna (non pubblica). L'uso di questa API può causare conflitti. Il modello descritto di seguito genera un errore se esiste già un acceleratore di tipi con lo stesso nome quando si importa il modulo. Rimuove anche gli acceleratori di tipo quando si rimuove il modulo dalla sessione.
Questo modello garantisce che i tipi siano disponibili in una sessione. Non influisce su IntelliSense o sul completamento durante la creazione di un file di script in VS Code.
Per ottenere IntelliSense e suggerimenti di completamento per i tipi personalizzati in VS Code, è necessario aggiungere un'istruzione using module
all'inizio dello script.
Il modello seguente illustra come registrare classi ed enumerazioni di PowerShell come acceleratori di tipi in un modulo. Aggiungere il frammento di codice al modulo script radice dopo qualsiasi definizione di tipo. Assicurarsi che la $ExportableTypes
variabile contenga ognuno dei tipi da rendere disponibili agli utenti quando importano il modulo. L'altro codice non richiede alcuna modifica.
# Define the types to export with type accelerators.
$ExportableTypes =@(
[DefinedTypeName]
)
# Get the internal TypeAccelerators class to use its static methods.
$TypeAcceleratorsClass = [psobject].Assembly.GetType(
'System.Management.Automation.TypeAccelerators'
)
# Ensure none of the types would clobber an existing type accelerator.
# If a type accelerator with the same name exists, throw an exception.
$ExistingTypeAccelerators = $TypeAcceleratorsClass::Get
foreach ($Type in $ExportableTypes) {
if ($Type.FullName -in $ExistingTypeAccelerators.Keys) {
$Message = @(
"Unable to register type accelerator '$($Type.FullName)'"
'Accelerator already exists.'
) -join ' - '
throw [System.Management.Automation.ErrorRecord]::new(
[System.InvalidOperationException]::new($Message),
'TypeAcceleratorAlreadyExists',
[System.Management.Automation.ErrorCategory]::InvalidOperation,
$Type.FullName
)
}
}
# Add type accelerators for every exportable type.
foreach ($Type in $ExportableTypes) {
$TypeAcceleratorsClass::Add($Type.FullName, $Type)
}
# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
foreach($Type in $ExportableTypes) {
$TypeAcceleratorsClass::Remove($Type.FullName)
}
}.GetNewClosure()
Quando gli utenti importano il modulo, tutti i tipi aggiunti agli acceleratori di tipo per la sessione sono immediatamente disponibili per IntelliSense e il completamento. Quando il modulo viene rimosso, quindi sono gli acceleratori di tipo.
Importazione manuale 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.
Se un modulo definisce classi ed enumerazioni ma non aggiunge acceleratori di tipi per tali tipi, usare un'istruzione using module
per importarle.
L'istruzione using module
importa classi ed enumerazioni dal modulo radice (ModuleToProcess
) di un modulo script o di un modulo binario. Non importa in modo coerente le classi definite in moduli annidati o classi definite negli script con origine punto nel modulo radice. Definire le classi che si desidera essere disponibili per gli utenti esterni al modulo direttamente nel modulo radice.
Per altre informazioni sull'istruzione using
, vedere about_Using.
Caricamento di codice appena modificato durante lo sviluppo
Durante lo sviluppo di un modulo di script, è comune apportare modifiche al codice e quindi caricare la nuova versione del modulo usando Import-Module
con il parametro Force . Il ricaricamento del modulo funziona solo per le modifiche apportate alle funzioni nel modulo radice. Import-Module
non ricarica i moduli annidati. Inoltre, non è possibile caricare classi aggiornate.
Per assicurarsi di eseguire la versione più recente, è necessario avviare una nuova sessione.
Le classi e le enumerazioni definite in PowerShell e importate con un'istruzione using
non possono essere scaricate.
Un'altra pratica di sviluppo comune consiste nel separare il codice in file diversi. Se si dispone di una funzione in un file che usa classi definite in un altro modulo, è necessario usare l'istruzione using module
per assicurarsi che le funzioni abbiano le definizioni di classe necessarie.
Il tipo PSReference non è supportato con i membri della classe
L'acceleratore [ref]
di tipi è abbreviato per la classe PSReference . L'uso di [ref]
per eseguire il cast di tipi di un membro della classe ha esito negativo in modo invisibile all'utente. Le API che usano [ref]
parametri non possono essere usate con i membri della classe. La classe PSReference è stata progettata per supportare oggetti COM. Gli oggetti COM hanno casi in cui è necessario passare un valore in per riferimento.
Per altre informazioni, vedere Classe PSReference.
Limiti
Gli elenchi seguenti includono limitazioni per la definizione delle classi di PowerShell e la soluzione alternativa per tali limitazioni, se presenti.
Limitazioni generali
I membri della classe non possono usare PSReference come tipo.
Soluzione alternativa: nessuna.
Non è possibile scaricare o ricaricare le classi di PowerShell in una sessione.
Soluzione alternativa: avviare una nuova sessione.
Le classi di PowerShell definite in un modulo non vengono importate automaticamente.
Soluzione alternativa: aggiungere i tipi definiti all'elenco di acceleratori di tipo nel modulo radice. In questo modo i tipi sono disponibili nell'importazione del modulo.
Le
hidden
parole chiave estatic
si applicano solo ai membri della classe, non a una definizione di classe.Soluzione alternativa: nessuna.
Limitazioni del costruttore
Il concatenamento del costruttore non è implementato.
Soluzione alternativa: definire i metodi nascosti
Init()
e chiamarli dall'interno dei costruttori.I parametri del costruttore non possono usare attributi, inclusi gli attributi di convalida.
Soluzione alternativa: riassegnare i parametri nel corpo del costruttore con l'attributo di convalida.
I parametri del costruttore non possono definire i valori predefiniti. I parametri sono sempre obbligatori.
Soluzione alternativa: nessuna.
Se un overload di un costruttore è nascosto, ogni overload per il costruttore viene considerato anche nascosto.
Soluzione alternativa: nessuna.
Limitazioni dei metodi
I parametri del metodo non possono usare attributi, inclusi gli attributi di convalida.
Soluzione alternativa: riassegnare i parametri nel corpo del metodo con l'attributo di convalida o definire il metodo nel costruttore statico con il
Update-TypeData
cmdlet .I parametri del metodo non possono definire valori predefiniti. I parametri sono sempre obbligatori.
Soluzione alternativa: definire il metodo nel costruttore statico con il
Update-TypeData
cmdlet .I metodi sono sempre pubblici, anche quando sono nascosti. Possono essere sottoposti a override quando la classe viene ereditata.
Soluzione alternativa: nessuna.
Se un overload di un metodo è nascosto, ogni overload per tale metodo viene considerato anche nascosto.
Soluzione alternativa: nessuna.
Limitazioni delle proprietà
Le proprietà statiche sono sempre modificabili. Le classi di PowerShell non possono definire proprietà statiche non modificabili.
Soluzione alternativa: nessuna.
Le proprietà non possono usare l'attributo ValidateScript, perché gli argomenti dell'attributo della proprietà della classe devono essere costanti.
Soluzione alternativa: definire una classe che eredita dal tipo ValidateArgumentsAttribute e usare tale attributo.
Le proprietà dichiarate direttamente non possono definire implementazioni getter e setter personalizzate.
Soluzione alternativa: definire una proprietà nascosta e usare
Update-TypeData
per definire la logica getter e setter visibile.Le proprietà non possono usare l'attributo Alias . L'attributo si applica solo a parametri, cmdlet e funzioni.
Soluzione alternativa: usare il
Update-TypeData
cmdlet per definire gli alias nei costruttori della classe.Quando una classe di PowerShell viene convertita in JSON con il
ConvertTo-Json
cmdlet , il codice JSON di output include tutte le proprietà nascoste e i relativi valori.Soluzione alternativa: nessuna
Limitazioni di ereditarietà
PowerShell non supporta la definizione di interfacce nel codice script.
Soluzione alternativa: definire le interfacce in C# e fare riferimento all'assembly che definisce le interfacce.
Le classi di PowerShell possono ereditare solo da una classe di base.
Soluzione alternativa: l'ereditarietà della classe è transitiva. Una classe derivata può ereditare da un'altra classe derivata per ottenere le proprietà e i metodi di una classe base.
Quando eredita da una classe o un'interfaccia generica, il parametro di tipo per il generico deve essere già definito. Una classe non può definirsi come parametro di tipo per una classe o un'interfaccia.
Soluzione alternativa: per derivare da una classe o un'interfaccia di base generica, definire il tipo personalizzato in un file diverso
.psm1
e usare l'istruzioneusing module
per caricare il tipo. Non esiste una soluzione alternativa per un tipo personalizzato da usare come parametro di tipo quando eredita da un generico.