Share via


about_Classes_Inheritance

Descrizione breve

Viene descritto come definire classi che estendono altri tipi.

Descrizione lunga

Le classi di PowerShell supportano l'ereditarietà, che consente di definire una classe figlio che riutilizza (eredita), estende o modifica il comportamento di una classe padre. La classe i cui membri vengono ereditati è denominata classe di base. Quella che eredita i membri della classe di base è denominata classe derivata.

PowerShell supporta solo l'ereditarietà singola. Una classe può ereditare solo da una singola classe. L'ereditarietà tuttavia è transitiva, pertanto è possibile definire una gerarchia di ereditarietà per un set di tipi. In altre parole, il tipo D può ereditare dal tipo C, che eredita dal tipo B, che eredita dal tipo di classe base A. Poiché l'ereditarietà è transitiva, i membri del tipo A sono disponibili per il tipo D.

Le classi derivate non ereditano tutti i membri della classe base. I membri seguenti non vengono ereditati:

  • Costruttori statici, che inizializzano i dati statici di una classe.
  • Costruttori di istanze, che vengono chiamati per creare una nuova istanza della classe. Ogni classe deve definire propri costruttori.

È 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.

Le classi possono anche ereditare da interfacce, che definiscono un contratto. Una classe che eredita da un'interfaccia deve implementare tale contratto. Quando lo fa, la classe è utilizzabile come qualsiasi altra classe che implementa tale interfaccia. Se una classe eredita da un'interfaccia ma non implementa l'interfaccia, PowerShell genera un errore di analisi per la classe .

Alcuni operatori di PowerShell dipendono da una classe che implementa un'interfaccia specifica. Ad esempio, l'operatore -eq controlla solo l'uguaglianza dei riferimenti, a meno che la classe non implementi l'interfaccia System.IEquatable . Gli -leoperatori , -lt, -gee -gt funzionano solo sulle classi che implementano l'interfaccia System.IComparable .

Una classe derivata usa la : sintassi per estendere una classe di base o implementare le interfacce. La classe derivata deve essere sempre all'estrema sinistra nella dichiarazione di classe.

In questo esempio viene illustrata la sintassi di ereditarietà della classe PowerShell di base.

Class Derived : Base {...}

Questo esempio mostra l'ereditarietà con una dichiarazione di interfaccia successiva alla classe base.

Class Derived : Base, Interface {...}

Sintassi

L'ereditarietà della classe usa le sintassi seguenti:

Sintassi di una riga

class <derived-class-name> : <base-class-or-interface-name>[, <interface-name>...] {
    <derived-class-body>
}

Ad esempio:

# Base class only
class Derived : Base {...}
# Interface only
class Derived : System.IComparable {...}
# Base class and interface
class Derived : Base, System.IComparable {...}

Sintassi multilinea

class <derived-class-name> : <base-class-or-interface-name>[,
    <interface-name>...] {
    <derived-class-body>
}

Ad esempio:

class Derived : Base,
                System.IComparable,
                System.IFormattable,
                System.IConvertible {
    # Derived class definition
}

Esempi

Esempio 1- Eredita ed esegue l'override da una classe di base

Nell'esempio seguente viene illustrato il comportamento delle proprietà ereditate con e senza eseguire l'override. Eseguire i blocchi di codice in ordine dopo aver letto la descrizione.

Definizione della classe base

Il primo blocco di codice definisce PublishedWork come classe base. Ha due proprietà statiche, List e Artists. Successivamente, definisce il metodo statico per aggiungere opere alla proprietà List statica RegisterWork() e agli artisti alla proprietà Artists, scrivendo un messaggio per ogni nuova voce negli elenchi.

La classe definisce tre proprietà di istanza che descrivono un lavoro pubblicato. Definisce infine i metodi di Register() istanza e ToString() .

class PublishedWork {
    static [PublishedWork[]] $List    = @()
    static [string[]]        $Artists = @()

    static [void] RegisterWork([PublishedWork]$Work) {
        $wName   = $Work.Name
        $wArtist = $Work.Artist
        if ($Work -notin [PublishedWork]::List) {
            Write-Verbose "Adding work '$wName' to works list"
            [PublishedWork]::List += $Work
        } else {
            Write-Verbose "Work '$wName' already registered."
        }
        if ($wArtist -notin [PublishedWork]::Artists) {
            Write-Verbose "Adding artist '$wArtist' to artists list"
            [PublishedWork]::Artists += $wArtist
        } else {
            Write-Verbose "Artist '$wArtist' already registered."
        }
    }

    static [void] ClearRegistry() {
        Write-Verbose "Clearing PublishedWork registry"
        [PublishedWork]::List    = @()
        [PublishedWork]::Artists = @()
    }

    [string] $Name
    [string] $Artist
    [string] $Category

    [void] Init([string]$WorkType) {
        if ([string]::IsNullOrEmpty($this.Category)) {
            $this.Category = "${WorkType}s"
        }
    }

    PublishedWork() {
        $WorkType = $this.GetType().FullName
        $this.Init($WorkType)
        Write-Verbose "Defined a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist as a published work of type [$WorkType]"
    }

    PublishedWork([string]$Name, [string]$Artist, [string]$Category) {
        $WorkType    = $this.GetType().FullName
        $this.Name   = $Name
        $this.Artist = $Artist
        $this.Init($WorkType)

        Write-Verbose "Defined '$Name' by $Artist ($Category) as a published work of type [$WorkType]"
    }

    [void]   Register() { [PublishedWork]::RegisterWork($this) }
    [string] ToString() { return "$($this.Name) by $($this.Artist)" }
}

Definizione di una classe derivata senza override

La prima classe derivata è Album. Non esegue l'override di proprietà o metodi. Aggiunge una nuova proprietà dell'istanza, Genres, che non esiste nella classe di base.

class Album : PublishedWork {
    [string[]] $Genres   = @()
}

Il blocco di codice seguente mostra il comportamento della classe Album derivata. Prima di tutto, imposta l'oggetto $VerbosePreference in modo che i messaggi dei metodi della classe emettano nella console. Crea tre istanze della classe, le mostra in una tabella e quindi le registra con il metodo statico RegisterWork() ereditato. Chiama quindi direttamente lo stesso metodo statico sulla classe base.

$VerbosePreference = 'Continue'
$Albums = @(
    [Album]@{
        Name   = 'The Dark Side of the Moon'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Psychedelic rock'
    }
    [Album]@{
        Name   = 'The Wall'
        Artist = 'Pink Floyd'
        Genres = 'Progressive rock', 'Art rock'
    }
    [Album]@{
        Name   = '36 Chambers'
        Artist = 'Wu-Tang Clan'
        Genres = 'Hip hop'
    }
)

$Albums | Format-Table
$Albums | ForEach-Object { [Album]::RegisterWork($_) }
$Albums | ForEach-Object { [PublishedWork]::RegisterWork($_) }
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]
VERBOSE: Defined a published work of type [Album]

Genres                               Name                      Artist       Category
------                               ----                      ------       --------
{Progressive rock, Psychedelic rock} The Dark Side of the Moon Pink Floyd   Albums
{Progressive rock, Art rock}         The Wall                  Pink Floyd   Albums
{Hip hop}                            36 Chambers               Wu-Tang Clan Albums

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

VERBOSE: Work 'The Dark Side of the Moon' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work 'The Wall' already registered.
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Work '36 Chambers' already registered.
VERBOSE: Artist 'Wu-Tang Clan' already registered.

Si noti che anche se la classe Album non ha definito un valore per Category o per alcun costruttore, la proprietà è stata definita dal costruttore predefinito della classe base.

Nella messaggistica dettagliata, la seconda chiamata al RegisterWork() metodo segnala che le opere e gli artisti sono già registrati. Anche se la prima chiamata a RegisterWork() era per la classe Album derivata, usava il metodo statico ereditato dalla classe PublishedWork di base. Tale metodo ha aggiornato le proprietà statiche List e Artist nella classe di base, che la classe derivata non ha eseguito l'override.

Il blocco di codice successivo cancella il Registro di sistema e chiama il Register() metodo di istanza sugli oggetti Album .

[PublishedWork]::ClearRegistry()
$Albums.Register()
VERBOSE: Clearing PublishedWork registry

VERBOSE: Adding work 'The Dark Side of the Moon' to works list
VERBOSE: Adding artist 'Pink Floyd' to artists list
VERBOSE: Adding work 'The Wall' to works list
VERBOSE: Artist 'Pink Floyd' already registered.
VERBOSE: Adding work '36 Chambers' to works list
VERBOSE: Adding artist 'Wu-Tang Clan' to artists list

Il metodo di istanza sugli oggetti Album ha lo stesso effetto della chiamata del metodo statico nella classe derivata o di base.

Il blocco di codice seguente confronta le proprietà statiche per la classe di base e la classe derivata, mostrando che sono uguali.

[pscustomobject]@{
    '[PublishedWork]::List'    = [PublishedWork]::List -join ",`n"
    '[Album]::List'            = [Album]::List -join ",`n"
    '[PublishedWork]::Artists' = [PublishedWork]::Artists -join ",`n"
    '[Album]::Artists'         = [Album]::Artists -join ",`n"
    'IsSame::List'             = (
        [PublishedWork]::List.Count -eq [Album]::List.Count -and
        [PublishedWork]::List.ToString() -eq [Album]::List.ToString()
    )
    'IsSame::Artists'          = (
        [PublishedWork]::Artists.Count -eq [Album]::Artists.Count -and
        [PublishedWork]::Artists.ToString() -eq [Album]::Artists.ToString()
    )
} | Format-List
[PublishedWork]::List    : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[Album]::List            : The Dark Side of the Moon by Pink Floyd,
                           The Wall by Pink Floyd,
                           36 Chambers by Wu-Tang Clan
[PublishedWork]::Artists : Pink Floyd,
                           Wu-Tang Clan
[Album]::Artists         : Pink Floyd,
                           Wu-Tang Clan
IsSame::List             : True
IsSame::Artists          : True

Definizione di una classe derivata con override

Il blocco di codice successivo definisce la classe Illustration che eredita dalla classe PublishedWork di base. La nuova classe estende la classe base definendo la proprietà dell'istanza Medium con un valore predefinito .Unknown

A differenza della classe Album derivata, Illustration esegue l'override delle proprietà e dei metodi seguenti:

  • Esegue l'override della proprietà Static Artists . La definizione è la stessa, ma la classe Illustration la dichiara direttamente.
  • Esegue l'override della proprietà dell'istanza Category , impostando il valore predefinito su Illustrations.
  • Esegue l'override del ToString() metodo di istanza in modo che la rappresentazione di stringa di un'illustrazione includa il supporto con cui è stato creato.

La classe definisce anche il metodo statico per chiamare prima il metodo della classe RegisterWork() di base e quindi aggiungere l'artista alla proprietà statica RegisterIllustration() Artists sottoposta a override nella classe derivata.

Infine, la classe esegue l'override di tutti e tre i costruttori:

  1. Il costruttore predefinito è vuoto, ad eccezione di un messaggio dettagliato che indica che è stata creata un'illustrazione.
  2. Il costruttore successivo accetta due valori stringa per il nome e l'artista che ha creato l'illustrazione. Anziché implementare la logica per l'impostazione delle proprietà Name e Artist , il costruttore chiama il costruttore appropriato dalla classe di base.
  3. L'ultimo costruttore accetta tre valori stringa per il nome, l'artista e il supporto dell'illustrazione. Entrambi i costruttori scrivono un messaggio dettagliato che indica che hanno creato un'illustrazione.
class Illustration : PublishedWork {
    static [string[]] $Artists = @()

    static [void] RegisterIllustration([Illustration]$Work) {
        $wArtist = $Work.Artist

        [PublishedWork]::RegisterWork($Work)

        if ($wArtist -notin [Illustration]::Artists) {
            Write-Verbose "Adding illustrator '$wArtist' to artists list"
            [Illustration]::Artists += $wArtist
        } else {
            Write-Verbose "Illustrator '$wArtist' already registered."
        }
    }

    [string] $Category = 'Illustrations'
    [string] $Medium   = 'Unknown'

    [string] ToString() {
        return "$($this.Name) by $($this.Artist) ($($this.Medium))"
    }

    Illustration() {
        Write-Verbose 'Defined an illustration'
    }

    Illustration([string]$Name, [string]$Artist) : base($Name, $Artist) {
        Write-Verbose "Defined '$Name' by $Artist ($($this.Medium)) as an illustration"
    }

    Illustration([string]$Name, [string]$Artist, [string]$Medium) {
        $this.Name = $Name
        $this.Artist = $Artist
        $this.Medium = $Medium

        Write-Verbose "Defined '$Name' by $Artist ($Medium) as an illustration"
    }
}

Il blocco di codice seguente illustra il comportamento della classe Illustration derivata. Crea tre istanze della classe, le mostra in una tabella e quindi le registra con il metodo statico RegisterWork() ereditato. Chiama quindi direttamente lo stesso metodo statico sulla classe base. Infine, scrive messaggi che mostrano l'elenco di artisti registrati per la classe base e la classe derivata.

$Illustrations = @(
    [Illustration]@{
        Name   = 'The Funny Thing'
        Artist = 'Wanda Gág'
        Medium = 'Lithography'
    }
    [Illustration]::new('Millions of Cats', 'Wanda Gág')
    [Illustration]::new(
      'The Lion and the Mouse',
      'Jerry Pinkney',
      'Watercolor'
    )
)

$Illustrations | Format-Table
$Illustrations | ForEach-Object { [Illustration]::RegisterIllustration($_) }
$Illustrations | ForEach-Object { [PublishedWork]::RegisterWork($_) }
"Published work artists: $([PublishedWork]::Artists -join ', ')"
"Illustration artists: $([Illustration]::Artists -join ', ')"
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined an illustration
VERBOSE: Defined 'Millions of Cats' by Wanda Gág as a published work of type [Illustration]
VERBOSE: Defined 'Millions of Cats' by Wanda Gág (Unknown) as an illustration
VERBOSE: Defined a published work of type [Illustration]
VERBOSE: Defined 'The Lion and the Mouse' by Jerry Pinkney (Watercolor) as an illustration

Category      Medium      Name                   Artist
--------      ------      ----                   ------
Illustrations Lithography The Funny Thing        Wanda Gág
Illustrations Unknown     Millions of Cats       Wanda Gág
Illustrations Watercolor  The Lion and the Mouse Jerry Pinkney

VERBOSE: Adding work 'The Funny Thing' to works list
VERBOSE: Adding artist 'Wanda Gág' to artists list
VERBOSE: Adding illustrator 'Wanda Gág' to artists list
VERBOSE: Adding work 'Millions of Cats' to works list
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Illustrator 'Wanda Gág' already registered.
VERBOSE: Adding work 'The Lion and the Mouse' to works list
VERBOSE: Adding artist 'Jerry Pinkney' to artists list
VERBOSE: Adding illustrator 'Jerry Pinkney' to artists list

VERBOSE: Work 'The Funny Thing' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'Millions of Cats' already registered.
VERBOSE: Artist 'Wanda Gág' already registered.
VERBOSE: Work 'The Lion and the Mouse' already registered.
VERBOSE: Artist 'Jerry Pinkney' already registered.

Published work artists: Pink Floyd, Wu-Tang Clan, Wanda Gág, Jerry Pinkney

Illustration artists: Wanda Gág, Jerry Pinkney

La messaggistica dettagliata dalla creazione delle istanze mostra che:

  • Quando si crea la prima istanza, il costruttore predefinito della classe base è stato chiamato prima del costruttore predefinito della classe derivata.
  • Quando si crea la seconda istanza, il costruttore ereditato in modo esplicito è stato chiamato per la classe base prima del costruttore della classe derivata.
  • Quando si crea la terza istanza, il costruttore predefinito della classe base è stato chiamato prima del costruttore della classe derivata.

I messaggi dettagliati del RegisterWork() metodo indicano che le opere e gli artisti sono già stati registrati. Questo perché il RegisterIllustration() metodo ha chiamato il RegisterWork() metodo internamente.

Tuttavia, quando si confronta il valore della proprietà static Artist sia per la classe di base che per la classe derivata, i valori sono diversi. La proprietà Artists per la classe derivata include solo illustratori, non gli artisti dell'album. La ridefinizione della proprietà Artist nella classe derivata impedisce alla classe di restituire la proprietà statica nella classe di base.

Il blocco di codice finale chiama il ToString() metodo sulle voci della proprietà List statica nella classe di base.

[PublishedWork]::List | ForEach-Object -Process { $_.ToString() }
The Dark Side of the Moon by Pink Floyd
The Wall by Pink Floyd
36 Chambers by Wu-Tang Clan
The Funny Thing by Wanda Gág (Lithography)
Millions of Cats by Wanda Gág (Unknown)
The Lion and the Mouse by Jerry Pinkney (Watercolor)

Le istanze album restituiscono solo il nome e l'artista nella stringa. Le istanze di Illustrazione includevano anche il supporto tra parentesi, perché tale classe sovraseda il ToString() metodo .

Esempio 2 - Implementazione di interfacce

Nell'esempio seguente viene illustrato come una classe può implementare una o più interfacce. L'esempio estende la definizione di una classe Temperature per supportare più operazioni e comportamenti.

Definizione della classe iniziale

Prima di implementare qualsiasi interfaccia, la classe Temperature viene definita con due proprietà, Gradi e Scala. Definisce costruttori e tre metodi di istanza per restituire l'istanza come gradi di una determinata scala.

La classe definisce le scale disponibili con l'enumerazione TemperatureScale .

class Temperature {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale   = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5/9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5/9 }
            Kelvin     { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius    { return $this.Degrees * 9/5 + 32 }
            Kelvin     { return $this.Degrees * 9/5 - 459.67 }
        }
        return $this.Degrees
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

In questa implementazione di base, tuttavia, esistono alcune limitazioni, come illustrato nell'output di esempio seguente:

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new([TemperatureScale]::Fahrenheit)
$Kelvin     = [Temperature]::new(0, 'Kelvin')

$Celsius, $Fahrenheit, $Kelvin

"The temperatures are: $Celsius, $Fahrenheit, $Kelvin"

[Temperature]::new() -eq $Celsius

$Celsius -gt $Kelvin
Degrees      Scale
-------      -----
   0.00    Celsius
   0.00 Fahrenheit
   0.00     Kelvin

The temperatures are: Temperature, Temperature, Temperature

False

InvalidOperation:
Line |
  11 |  $Celsius -gt $Kelvin
     |  ~~~~~~~~~~~~~~~~~~~~
     | Cannot compare "Temperature" because it is not IComparable.

L'output mostra che le istanze di Temperature:

  • Non visualizzare correttamente come stringhe.
  • Non è possibile verificare correttamente l'equivalenza.
  • Non è possibile confrontare.

Questi tre problemi possono essere risolti implementando interfacce per la classe .

Implementazione di IFormattable

La prima interfaccia da implementare per la classe Temperature è System.IFormattable. Questa interfaccia abilita la formattazione di un'istanza della classe come stringhe diverse. Per implementare l'interfaccia, la classe deve ereditare da System.IFormattable e definire il metodo di ToString() istanza.

Il ToString() metodo di istanza deve avere la firma seguente:

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # Implementation
}

La firma richiesta dall'interfaccia è elencata nella documentazione di riferimento.

Per Temperature, la classe deve supportare tre formati: C per restituire l'istanza in Celsius, F per restituirla in Fahrenheit e K per restituirla in Kelvin. Per qualsiasi altro formato, il metodo deve generare un'eccezione System.FormatException.

[string] ToString(
    [string]$Format,
    [System.IFormatProvider]$FormatProvider
) {
    # If format isn't specified, use the defined scale.
    if ([string]::IsNullOrEmpty($Format)) {
        $Format = switch ($this.Scale) {
            Celsius    { 'C' }
            Fahrenheit { 'F' }
            Kelvin     { 'K' }
        }
    }
    # If format provider isn't specified, use the current culture.
    if ($null -eq $FormatProvider) {
        $FormatProvider = [CultureInfo]::CurrentCulture
    }
    # Format the temperature.
    switch ($Format) {
        'C' {
            return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
        }
        'F' {
            return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
        }
        'K' {
            return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
        }
    }
    # If we get here, the format is invalid.
    throw [System.FormatException]::new(
        "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
    )
}

In questa implementazione, il metodo usa per impostazione predefinita la scala delle istanze per il formato e le impostazioni cultura correnti durante la formattazione del valore numerico del grado stesso. Usa i metodi di To<Scale>() istanza per convertire i gradi, formattarli in posizioni decimali e accoda il simbolo di grado appropriato alla stringa.

Con la firma richiesta implementata, la classe può anche definire overload per semplificare la restituzione dell'istanza formattata.

[string] ToString([string]$Format) {
    return $this.ToString($Format, $null)
}

[string] ToString() {
    return $this.ToString($null, $null)
}

Il codice seguente illustra la definizione aggiornata per Temperature:

class Temperature : System.IFormattable {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

L'output per gli overload del metodo è illustrato nel blocco seguente.

$Temp = [Temperature]::new()
"The temperature is $Temp"
$Temp.ToString()
$Temp.ToString('K')
$Temp.ToString('F', $null)
The temperature is 0.00°C

0.00°C

273.15°K

32.00°F

Implementazione di IEquatable

Ora che la classe Temperature può essere formattata per la leggibilità, gli utenti devono essere in grado di verificare se due istanze della classe sono uguali. Per supportare questo test, la classe deve implementare l'interfaccia System.IEquatable .

Per implementare l'interfaccia, la classe deve ereditare da System.IEquatable e definire il metodo di Equals() istanza. Il Equals() metodo deve avere la firma seguente:

[bool] Equals([object]$Other) {
    # Implementation
}

La firma richiesta dall'interfaccia è elencata nella documentazione di riferimento.

Per Temperature, la classe deve supportare solo il confronto di due istanze della classe . Per qualsiasi altro valore o tipo, incluso $null, deve restituire $false. Quando si confrontano due temperature, il metodo deve convertire entrambi i valori in Kelvin, poiché le temperature possono essere equivalenti anche con scale diverse.

[bool] Equals([object]$Other) {
    # If the other object is null, we can't compare it.
    if ($null -eq $Other) {
        return $false
    }

    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        return $false
    }

    # Compare the temperatures as Kelvin.
    return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
}

Con il metodo di interfaccia implementato, la definizione aggiornata per Temperature è:

class Temperature : System.IFormattable, System.IEquatable[object] {
    [float]            $Degrees
    [TemperatureScale] $Scale

    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }

        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }

        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

Il blocco seguente illustra il comportamento della classe aggiornata:

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit) = $($Celsius.Equals($Fahrenheit))
`$Celsius -eq `$Fahrenheit     = $($Celsius -eq $Fahrenheit)
`$Celsius -ne `$Kelvin         = $($Celsius -ne $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K

$Celsius.Equals($Fahrenheit) = True
$Celsius -eq $Fahrenheit     = True
$Celsius -ne $Kelvin         = True

Implementazione di IComparable

L'ultima interfaccia da implementare per la classe Temperature è System.IComparable. Quando la classe implementa questa interfaccia, gli utenti possono usare gli -ltoperatori , -le, -gte -ge per confrontare le istanze della classe .

Per implementare l'interfaccia, la classe deve ereditare da System.IComparable e definire il metodo di Equals() istanza. Il Equals() metodo deve avere la firma seguente:

[int] CompareTo([Object]$Other) {
    # Implementation
}

La firma richiesta dall'interfaccia è elencata nella documentazione di riferimento.

Per Temperature, la classe deve supportare solo il confronto di due istanze della classe . Poiché il tipo sottostante per la proprietà Degrees , anche se convertito in una scala diversa, è un numero a virgola mobile, il metodo può basarsi sul tipo sottostante per il confronto effettivo.

[int] CompareTo([object]$Other) {
    # If the other object's null, consider this instance "greater than" it
    if ($null -eq $Other) {
        return 1
    }
    # If the other object isn't a temperature, we can't compare it.
    $OtherTemperature = $Other -as [Temperature]
    if ($null -eq $OtherTemperature) {
        throw [System.ArgumentException]::new(
            "Object must be of type 'Temperature'."
        )
    }
    # Compare the temperatures as Kelvin.
    return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
}

La definizione finale per la classe Temperature è:

class Temperature : System.IFormattable,
                    System.IComparable,
                    System.IEquatable[object] {
    # Instance properties
    [float]            $Degrees
    [TemperatureScale] $Scale

    # Constructors
    Temperature() {}
    Temperature([float] $Degrees)          { $this.Degrees = $Degrees }
    Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
    Temperature([float] $Degrees, [TemperatureScale] $Scale) {
        $this.Degrees = $Degrees
        $this.Scale = $Scale
    }

    [float] ToKelvin() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees + 273.15 }
            Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
        }
        return $this.Degrees
    }
    [float] ToCelsius() {
        switch ($this.Scale) {
            Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
            Kelvin { return $this.Degrees - 273.15 }
        }
        return $this.Degrees
    }
    [float] ToFahrenheit() {
        switch ($this.Scale) {
            Celsius { return $this.Degrees * 9 / 5 + 32 }
            Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
        }
        return $this.Degrees
    }

    [string] ToString(
        [string]$Format,
        [System.IFormatProvider]$FormatProvider
    ) {
        # If format isn't specified, use the defined scale.
        if ([string]::IsNullOrEmpty($Format)) {
            $Format = switch ($this.Scale) {
                Celsius    { 'C' }
                Fahrenheit { 'F' }
                Kelvin     { 'K' }
            }
        }
        # If format provider isn't specified, use the current culture.
        if ($null -eq $FormatProvider) {
            $FormatProvider = [CultureInfo]::CurrentCulture
        }
        # Format the temperature.
        switch ($Format) {
            'C' {
                return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
            }
            'F' {
                return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
            }
            'K' {
                return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
            }
        }
        # If we get here, the format is invalid.
        throw [System.FormatException]::new(
            "Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
        )
    }

    [string] ToString([string]$Format) {
        return $this.ToString($Format, $null)
    }

    [string] ToString() {
        return $this.ToString($null, $null)
    }

    [bool] Equals([object]$Other) {
        # If the other object is null, we can't compare it.
        if ($null -eq $Other) {
            return $false
        }
        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            return $false
        }
        # Compare the temperatures as Kelvin.
        return $this.ToKelvin() -eq $OtherTemperature.ToKelvin()
    }
    [int] CompareTo([object]$Other) {
        # If the other object's null, consider this instance "greater than" it
        if ($null -eq $Other) {
            return 1
        }
        # If the other object isn't a temperature, we can't compare it.
        $OtherTemperature = $Other -as [Temperature]
        if ($null -eq $OtherTemperature) {
            throw [System.ArgumentException]::new(
                "Object must be of type 'Temperature'."
            )
        }
        # Compare the temperatures as Kelvin.
        return $this.ToKelvin().CompareTo($OtherTemperature.ToKelvin())
    }
}

enum TemperatureScale {
    Celsius    = 0
    Fahrenheit = 1
    Kelvin     = 2
}

Con la definizione completa, gli utenti possono formattare e confrontare istanze della classe in PowerShell come qualsiasi tipo predefinito.

$Celsius    = [Temperature]::new()
$Fahrenheit = [Temperature]::new(32, 'Fahrenheit')
$Kelvin     = [Temperature]::new([TemperatureScale]::Kelvin)

@"
Temperatures are: $Celsius, $Fahrenheit, $Kelvin
`$Celsius.Equals(`$Fahrenheit)    = $($Celsius.Equals($Fahrenheit))
`$Celsius.Equals(`$Kelvin)        = $($Celsius.Equals($Kelvin))
`$Celsius.CompareTo(`$Fahrenheit) = $($Celsius.CompareTo($Fahrenheit))
`$Celsius.CompareTo(`$Kelvin)     = $($Celsius.CompareTo($Kelvin))
`$Celsius -lt `$Fahrenheit        = $($Celsius -lt $Fahrenheit)
`$Celsius -le `$Fahrenheit        = $($Celsius -le $Fahrenheit)
`$Celsius -eq `$Fahrenheit        = $($Celsius -eq $Fahrenheit)
`$Celsius -gt `$Kelvin            = $($Celsius -gt $Kelvin)
"@
Temperatures are: 0.00°C, 32.00°F, 0.00°K
$Celsius.Equals($Fahrenheit)    = True
$Celsius.Equals($Kelvin)        = False
$Celsius.CompareTo($Fahrenheit) = 0
$Celsius.CompareTo($Kelvin)     = 1
$Celsius -lt $Fahrenheit        = False
$Celsius -le $Fahrenheit        = True
$Celsius -eq $Fahrenheit        = True
$Celsius -gt $Kelvin            = True

Esempio 3- Eredita da una classe di base generica

Questo esempio illustra come derivare da una classe generica come System.Collections.Generic.List.

Uso di una classe predefinita come parametro di tipo

Eseguire il blocco di codice seguente. Mostra come una nuova classe può ereditare da un tipo generico purché il parametro di tipo sia già definito in fase di analisi.

class ExampleStringList : System.Collections.Generic.List[string] {}

$List = [ExampleStringList]::New()
$List.AddRange([string[]]@('a','b','c'))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleStringList
BaseType : System.Collections.Generic.List`1[System.String]

a
b
c

Uso di una classe personalizzata come parametro di tipo

Il blocco di codice successivo definisce innanzitutto una nuova classe, ExampleItem, con una singola proprietà dell'istanza e il ToString() metodo . Definisce quindi la classe ExampleItemList che eredita dalla classe base System.Collections.Generic.List con ExampleItem come parametro di tipo.

Copiare l'intero blocco di codice ed eseguirlo come singola istruzione.

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}
ParentContainsErrorRecordException: An error occurred while creating the pipeline.

L'esecuzione dell'intero blocco di codice genera un errore perché PowerShell non ha ancora caricato la classe ExampleItem nel runtime. Non è ancora possibile usare il nome della classe come parametro di tipo per la classe base System.Collections.Generic.List .

Eseguire i blocchi di codice seguenti nell'ordine in cui sono definiti.

class ExampleItem {
    [string] $Name
    [string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}

Questa volta, PowerShell non genera errori. Entrambe le classi sono ora definite. Eseguire il blocco di codice seguente per visualizzare il comportamento della nuova classe.

$List = [ExampleItemList]::New()
$List.AddRange([ExampleItem[]]@(
    [ExampleItem]@{ Name = 'Foo' }
    [ExampleItem]@{ Name = 'Bar' }
    [ExampleItem]@{ Name = 'Baz' }
))
$List.GetType() | Format-List -Property Name, BaseType
$List
Name     : ExampleItemList
BaseType : System.Collections.Generic.List`1[ExampleItem]

Name
----
Foo
Bar
Baz

Derivazione di un generico con un parametro di tipo personalizzato in un modulo

I blocchi di codice seguenti illustrano come definire una classe che eredita da una classe di base generica che usa un tipo personalizzato per il parametro di tipo.

Salvare il blocco di codice seguente come GenericExample.psd1.

@{
    RootModule        = 'GenericExample.psm1'
    ModuleVersion     = '0.1.0'
    GUID              = '2779fa60-0b3b-4236-b592-9060c0661ac2'
}

Salvare il blocco di codice seguente come GenericExample.InventoryItem.psm1.

class InventoryItem {
    [string] $Name
    [int]    $Count

    InventoryItem() {}
    InventoryItem([string]$Name) {
        $this.Name = $Name
    }
    InventoryItem([string]$Name, [int]$Count) {
        $this.Name  = $Name
        $this.Count = $Count
    }

    [string] ToString() {
        return "$($this.Name) ($($this.Count))"
    }
}

Salvare il blocco di codice seguente come GenericExample.psm1.

using namespace System.Collections.Generic
using module ./GenericExample.InventoryItem.psm1

class Inventory : List[InventoryItem] {}

# Define the types to export with type accelerators.
$ExportableTypes =@(
    [InventoryItem]
    [Inventory]
)
# 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()

Suggerimento

Il modulo radice aggiunge i tipi personalizzati agli acceleratori di tipi di PowerShell. Questo modello consente agli utenti del modulo di accedere immediatamente a IntelliSense e al completamento automatico per i tipi personalizzati senza dover prima usare l'istruzione using module .

Per altre informazioni su questo modello, vedere la sezione "Esportazione con acceleratori di tipi" di about_Classes.

Importare il modulo e verificare l'output.

Import-Module ./GenericExample.psd1

$Inventory = [Inventory]::new()
$Inventory.GetType() | Format-List -Property Name, BaseType

$Inventory.Add([InventoryItem]::new('Bucket', 2))
$Inventory.Add([InventoryItem]::new('Mop'))
$Inventory.Add([InventoryItem]@{ Name = 'Broom' ; Count = 4 })
$Inventory
Name     : Inventory
BaseType : System.Collections.Generic.List`1[InventoryItem]

Name   Count
----   -----
Bucket     2
Mop        0
Broom      4

Il modulo viene caricato senza errori perché la classe InventoryItem è definita in un file di modulo diverso rispetto alla classe Inventory . Entrambe le classi sono disponibili per gli utenti del modulo.

Ereditarietà di una classe base

Quando una classe eredita da una classe di base, eredita le proprietà e i metodi della classe base. Non eredita direttamente i costruttori della classe di base, ma può chiamarli.

Quando la classe base è definita in .NET anziché In PowerShell, tenere presente che:

  • Le classi di PowerShell non possono ereditare da classi sealed.
  • Quando eredita da una classe di base generica, il parametro di tipo per la classe generica non può essere la classe derivata. L'uso della classe derivata come parametro di tipo genera un errore di analisi.

Per informazioni sul funzionamento dell'ereditarietà e dell'override per le classi derivate, vedere l'esempio 1.

Costruttori di classi derivate

Le classi derivate non ereditano direttamente i costruttori della classe base. Se la classe base definisce un costruttore predefinito e la classe derivata non definisce costruttori, le nuove istanze della classe derivata usano il costruttore predefinito della classe base. Se la classe base non definisce un costruttore predefinito, la classe derivata deve definire in modo esplicito almeno un costruttore.

I costruttori di classi derivate possono richiamare un costruttore dalla classe base con la base parola chiave . Se la classe derivata non richiama in modo esplicito un costruttore dalla classe base, richiama invece il costruttore predefinito per la classe base.

Per richiamare un costruttore di base non predefinito, aggiungere : base(<parameters>) dopo i parametri del costruttore e prima del blocco del corpo.

class <derived-class> : <base-class> {
    <derived-class>(<derived-parameters>) : <base-class>(<base-parameters>) {
        # initialization code
    }
}

Quando si definisce un costruttore che chiama un costruttore di classe base, i parametri possono essere uno degli elementi seguenti:

  • Variabile di qualsiasi parametro nel costruttore della classe derivata.
  • Qualsiasi valore statico.
  • Qualsiasi espressione che restituisce un valore del tipo di parametro.

La classe Illustration nell'esempio 1 mostra come una classe derivata può usare i costruttori della classe base.

Metodi della classe derivata

Quando una classe deriva da una classe di base, eredita i metodi della classe di base e i relativi overload. Tutti gli overload di metodo definiti nella classe base, inclusi i metodi nascosti, sono disponibili nella classe derivata.

Una classe derivata può eseguire l'override di un overload di metodo ereditato ridefinendolo nella definizione della classe. Per eseguire l'override dell'overload, i tipi di parametro devono essere uguali a quello della classe base. Il tipo di output per l'overload può essere diverso.

A differenza dei costruttori, i metodi non possono usare la : base(<parameters>) sintassi per richiamare un overload della classe di base per il metodo . L'overload ridefinito nella classe derivata sostituisce completamente l'overload definito dalla classe base. Per chiamare il metodo della classe base per un'istanza di , eseguire il cast della variabile di istanza ($this) alla classe base prima di chiamare il metodo .

Il frammento di codice seguente mostra come una classe derivata può chiamare il metodo della classe base.

class BaseClass {
    [bool] IsTrue() { return $true }
}
class DerivedClass : BaseClass {
    [bool] IsTrue()     { return $false }
    [bool] BaseIsTrue() { return ([BaseClass]$this).IsTrue() }
}

@"
[BaseClass]::new().IsTrue()        = $([BaseClass]::new().IsTrue())
[DerivedClass]::new().IsTrue()     = $([DerivedClass]::new().IsTrue())
[DerivedClass]::new().BaseIsTrue() = $([DerivedClass]::new().BaseIsTrue())
"@
[BaseClass]::new().IsTrue()        = True
[DerivedClass]::new().IsTrue()     = False
[DerivedClass]::new().BaseIsTrue() = True

Per un esempio esteso che illustra come una classe derivata può eseguire l'override dei metodi ereditati, vedere la classe Illustration nell'esempio 1.

Proprietà della classe derivata

Quando una classe deriva da una classe di base, eredita le proprietà della classe base. Tutte le proprietà definite nella classe base, incluse le proprietà nascoste, sono disponibili nella classe derivata.

Una classe derivata può eseguire l'override di una proprietà ereditata definendola nella definizione della classe. La proprietà nella classe derivata utilizza il tipo ridefinito e il valore predefinito, se presenti. Se la proprietà ereditata ha definito un valore predefinito e la proprietà ridefinita non ha alcun valore predefinito.

Se una classe derivata non esegue l'override di una proprietà statica, l'accesso alla proprietà statica tramite la classe derivata accede alla proprietà statica della classe base. La modifica del valore della proprietà tramite la classe derivata modifica il valore nella classe base. Qualsiasi altra classe derivata che non esegue l'override della proprietà statica usa anche il valore della proprietà nella classe di base. L'aggiornamento del valore di una proprietà statica ereditata in una classe che non esegue l'override della proprietà potrebbe avere effetti imprevisti per le classi derivate dalla stessa classe di base.

Nell'esempio 1 viene illustrato come le classi derivate che ereditano, estendono ed eseguono l'override delle proprietà della classe di base.

Derivazione da generics

Quando una classe deriva da un oggetto generico, il parametro di tipo deve essere già definito prima che PowerShell analizzi la classe derivata. Se il parametro di tipo per il generico è una classe di PowerShell o un'enumerazione definita nello stesso file o blocco di codice, PowerShell genera un errore.

Per derivare una classe da una classe base generica con un tipo personalizzato come parametro di tipo, definire la classe o l'enumerazione per il parametro di tipo in un file o modulo diverso e usare l'istruzione using module per caricare la definizione del tipo.

Per un esempio che illustra come ereditare da una classe di base generica, vedere l'esempio 3.

Classi utili da ereditare

Esistono alcune classi che possono essere utili per ereditare durante la creazione di moduli di PowerShell. In questa sezione sono elencate alcune classi di base e le classi per cui è possibile usare una classe derivata.

  • System.Attribute : derivare classi per definire attributi che possono essere usati per variabili, parametri, definizioni di classe ed enumerazione e altro ancora.
  • System.Management.Automation.ArgumentTransformationAttribute : derivare le classi per gestire la conversione dell'input per una variabile o un parametro in un tipo di dati specifico.
  • System.Management.Automation.ValidateArgumentsAttribute : derivare classi per applicare la convalida personalizzata a variabili, parametri e proprietà di classe.
  • System.Collections.Generic.List - Derivare classi per semplificare la creazione e la gestione di elenchi di un tipo di dati specifico.
  • System.Exception : derivare classi per definire errori personalizzati.

Implementazione di interfacce

Una classe di PowerShell che implementa un'interfaccia deve implementare tutti i membri di tale interfaccia. L'omissione dei membri dell'interfaccia di implementazione causa un errore in fase di analisi nello script.

Nota

PowerShell non supporta la dichiarazione di nuove interfacce nello script di PowerShell. Al contrario, le interfacce devono essere dichiarate nel codice .NET e aggiunte alla sessione con il Add-Type cmdlet o l'istruzione using assembly .

Quando una classe implementa un'interfaccia, può essere usata come qualsiasi altra classe che implementa tale interfaccia. Alcuni comandi e operazioni limitano i tipi supportati alle classi che implementano un'interfaccia specifica.

Per esaminare un'implementazione di esempio di interfacce, vedere l'esempio 2.

Interfacce utili da implementare

Esistono alcune classi di interfaccia che possono essere utili per ereditare durante la creazione di moduli di PowerShell. In questa sezione sono elencate alcune classi di base e le classi per cui è possibile usare una classe derivata.

  • System.IEquatable : questa interfaccia consente agli utenti di confrontare due istanze della classe . Quando una classe non implementa questa interfaccia, PowerShell verifica la parità tra due istanze usando l'uguaglianza dei riferimenti. In altre parole, un'istanza della classe è uguale solo a se stessa, anche se i valori delle proprietà in due istanze sono uguali.
  • System.IComparable: questa interfaccia consente agli utenti di confrontare le istanze della classe con gli -leoperatori di confronto , -lt-ge, e -gt . Quando una classe non implementa questa interfaccia, tali operatori generano un errore.
  • System.IFormattable : questa interfaccia consente agli utenti di formattare le istanze della classe in stringhe diverse. Ciò è utile per le classi con più di una rappresentazione di stringa standard, ad esempio articoli di budget, bibliografie e temperature.
  • System.IConvertible : questa interfaccia consente agli utenti di convertire istanze della classe in altri tipi di runtime. Ciò è utile per le classi che hanno un valore numerico sottostante o possono essere convertite in una classe.

Limiti

  • 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'istruzione using 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.

Vedi anche