Share via


about_Classes_Inheritance

Korte beschrijving

Hierin wordt beschreven hoe u klassen kunt definiëren die andere typen uitbreiden.

Lange beschrijving

PowerShell-klassen ondersteunen overname, waarmee u een onderliggende klasse kunt definiëren die opnieuw wordt gebruikt (overgenomen), het gedrag van een bovenliggende klasse verlengt of wijzigt. De klasse waarvan de leden worden overgenomen, wordt de basisklasse genoemd. De klasse die de leden van de basisklasse over neemt, wordt de afgeleide klasse genoemd.

PowerShell biedt alleen ondersteuning voor één overname. Een klasse kan slechts overnemen van één klasse. Overname is echter transitief, waarmee u een overnamehiërarchie voor een set typen kunt definiëren. Met andere woorden, type D kan overnemen van type C, die wordt overgenomen van type B, die wordt overgenomen van het basisklassetype A. Omdat overname transitief is, zijn de leden van het type A beschikbaar voor type D.

Afgeleide klassen nemen niet alle leden van de basisklasse over. De volgende leden worden niet overgenomen:

  • Statische constructors, die de statische gegevens van een klasse initialiseren.
  • Instantieconstructors die u aanroept om een nieuw exemplaar van de klasse te maken. Elke klasse moet zijn eigen constructors definiëren.

U kunt een klasse uitbreiden door een nieuwe klasse te maken die is afgeleid van een bestaande klasse. De afgeleide klasse neemt de eigenschappen en methoden van de basisklasse over. U kunt de leden van de basisklasse toevoegen of overschrijven zoals vereist.

Klassen kunnen ook overnemen van interfaces, waarmee een contract wordt gedefinieerd. Een klasse die overkomt van een interface, moet dat contract implementeren. Wanneer dit het geval is, is de klasse bruikbaar als elke andere klasse die die interface implementeert. Als een klasse de interface over neemt van een interface, maar de interface niet implementeert, wordt in PowerShell een parseringsfout voor de klasse gegenereerd.

Sommige PowerShell-operators zijn afhankelijk van een klasse die een specifieke interface implementeert. De operator controleert bijvoorbeeld -eq alleen op gelijkheid van verwijzingen, tenzij de klasse de Interface System.IEquatable implementeert. De -leoperators , -ge-lten operators -gt werken alleen voor klassen die de System.IComparable-interface implementeren.

Een afgeleide klasse gebruikt de : syntaxis om een basisklasse uit te breiden of interfaces te implementeren. De afgeleide klasse moet altijd het meest links zijn in de klassedeclaratie.

In dit voorbeeld ziet u de basissyntaxis van De PowerShell-klasse.

Class Derived : Base {...}

In dit voorbeeld ziet u overname met een interfacedeclaratie die na de basisklasse komt.

Class Derived : Base, Interface {...}

Syntaxis

De overname van klassen maakt gebruik van de volgende syntaxis:

Syntaxis van één regel

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

Voorbeeld:

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

Syntaxis met meerdere regels

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

Voorbeeld:

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

Voorbeelden

Voorbeeld 1: Overnemen en overschrijven van een basisklasse

In het volgende voorbeeld ziet u het gedrag van overgenomen eigenschappen met en zonder te overschrijven. Voer de codeblokken op volgorde uit nadat u de beschrijving hebt gelezen.

De basisklasse definiëren

Het eerste codeblok definieert PublishedWork als basisklasse. Het heeft twee statische eigenschappen, List en Artists. Vervolgens definieert het de statische RegisterWork() methode om werken toe te voegen aan de eigenschap Static List en de kunstenaars aan de eigenschap Artists , waarbij een bericht wordt geschreven voor elke nieuwe vermelding in de lijsten.

De klasse definieert drie exemplaareigenschappen die een gepubliceerd werk beschrijven. Ten slotte worden de Register() methoden en ToString() instantiemethoden gedefinieerd.

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)" }
}

Een afgeleide klasse definiëren zonder onderdrukkingen

De eerste afgeleide klasse is Album. Er worden geen eigenschappen of methoden overschreven. Er wordt een nieuwe instantieeigenschap toegevoegd, Genres, die niet bestaat in de basisklasse.

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

In het volgende codeblok ziet u het gedrag van de afgeleide albumklasse . Eerst wordt hiermee ingesteld $VerbosePreference dat de berichten van de klassemethoden naar de console worden verzonden. Er worden drie exemplaren van de klasse gemaakt, weergegeven in een tabel en vervolgens geregistreerd bij de overgenomen statische RegisterWork() methode. Vervolgens wordt dezelfde statische methode rechtstreeks aangeroepen op de basisklasse.

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

U ziet dat hoewel de klasse Album geen waarde voor categorie of constructors heeft gedefinieerd, de eigenschap is gedefinieerd door de standaardconstructor van de basisklasse.

In de uitgebreide berichtgeving meldt de tweede aanroep van de RegisterWork() methode dat de werken en kunstenaars al zijn geregistreerd. Hoewel de eerste aanroep RegisterWork() voor de afgeleide albumklasse was, werd de overgenomen statische methode van de base PublishedWork-klasse gebruikt. Met deze methode zijn de statische eigenschappen List en Artist bijgewerkt op de basisklasse, die niet door de afgeleide klasse is overschreven.

Het volgende codeblok wist het register en roept de Register() exemplaarmethode op de albumobjecten aan.

[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

De instantiemethode voor de albumobjecten heeft hetzelfde effect als het aanroepen van de statische methode op de afgeleide of basisklasse.

In het volgende codeblok worden de statische eigenschappen voor de basisklasse en de afgeleide klasse vergeleken, waarmee wordt aangegeven dat ze hetzelfde zijn.

[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

Een afgeleide klasse definiëren met onderdrukkingen

In het volgende codeblok wordt de afbeeldingsklasse gedefinieerd die wordt overgenomen van de base PublishedWork-klasse . De nieuwe klasse breidt de basisklasse uit door de eigenschap Gemiddeld exemplaar te definiëren met een standaardwaarde van Unknown.

In tegenstelling tot de afgeleide albumklasseoverschrijft Illustratie de volgende eigenschappen en methoden:

  • Het overschrijft de eigenschap static Artists . De definitie is hetzelfde, maar de klasse Illustratie declareert deze rechtstreeks.
  • De eigenschap Categorie-instantie wordt overschreven, waarbij de standaardwaarde wordt ingesteld op Illustrations.
  • De instantiemethode wordt overschreven ToString() , zodat de tekenreeksweergave van een afbeelding het medium bevat waarmee deze is gemaakt.

De klasse definieert ook de statische RegisterIllustration() methode om eerst de basisklassemethode RegisterWork() aan te roepen en vervolgens de kunstenaar toe te voegen aan de overschreven statische eigenschap Artists op de afgeleide klasse.

Ten slotte overschrijft de klasse alle drie de constructors:

  1. De standaardconstructor is leeg, met uitzondering van een uitgebreid bericht dat aangeeft dat er een afbeelding is gemaakt.
  2. De volgende constructor heeft twee tekenreekswaarden voor de naam en kunstenaar die de illustratie heeft gemaakt. In plaats van de logica te implementeren voor het instellen van de eigenschappen Name en Artist , roept de constructor de juiste constructor aan vanuit de basisklasse.
  3. De laatste constructor heeft drie tekenreekswaarden voor de naam, kunstenaar en het medium van de afbeelding. Beide constructors schrijven een uitgebreid bericht dat aangeeft dat ze een afbeelding hebben gemaakt.
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"
    }
}

In het volgende codeblok ziet u het gedrag van de afgeleide illustratieklasse . Er worden drie exemplaren van de klasse gemaakt, weergegeven in een tabel en vervolgens geregistreerd bij de overgenomen statische RegisterWork() methode. Vervolgens wordt dezelfde statische methode rechtstreeks aangeroepen op de basisklasse. Ten slotte schrijft het berichten met de lijst met geregistreerde artiesten voor de basisklasse en de afgeleide klasse.

$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

De uitgebreide berichten van het maken van de exemplaren laten zien dat:

  • Bij het maken van het eerste exemplaar werd de standaardconstructor van de basisklasse aangeroepen vóór de standaardconstructor van de afgeleide klasse.
  • Bij het maken van het tweede exemplaar werd de expliciet overgenomen constructor aangeroepen voor de basisklasse vóór de afgeleide klasseconstructor.
  • Bij het maken van het derde exemplaar werd de standaardconstructor van de basisklasse aangeroepen vóór de afgeleide klasseconstructor.

De uitgebreide berichten van de RegisterWork() methode geven aan dat de werken en kunstenaars al zijn geregistreerd. Dit komt doordat de RegisterIllustration() methode intern de RegisterWork() methode aangeroepen.

Bij het vergelijken van de waarde van de eigenschap static Artist voor zowel de basisklasse als de afgeleide klasse, zijn de waarden echter anders. De eigenschap Artists voor de afgeleide klasse omvat alleen illustratoren, niet de albumkunstenaars. Als u de eigenschap Artist opnieuw definieert in de afgeleide klasse, voorkomt u dat de klasse de statische eigenschap op de basisklasse retourneert.

In het laatste codeblok wordt de ToString() methode aangeroepen voor de vermeldingen van de statische lijsteigenschap op de basisklasse.

[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)

De albumexemplaren retourneren alleen de naam en artiest in hun tekenreeks. De afbeeldingsexemplaren bevatten ook het medium tussen haakjes, omdat die klasse de ToString() methode overroest.

Voorbeeld 2: Interfaces implementeren

In het volgende voorbeeld ziet u hoe een klasse een of meer interfaces kan implementeren. In het voorbeeld wordt de definitie van een temperatuurklasse uitgebreid ter ondersteuning van meer bewerkingen en gedrag.

Eerste klassedefinitie

Voordat u interfaces implementeert, wordt de temperatuurklasse gedefinieerd met twee eigenschappen, Graden en Schaal. Er worden constructors en drie exemplaarmethoden gedefinieerd voor het retourneren van het exemplaar als mate van een bepaalde schaal.

De klasse definieert de beschikbare schalen met de opsomming 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 deze basis implementatie gelden echter enkele beperkingen, zoals wordt weergegeven in de volgende voorbeelduitvoer:

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

In de uitvoer ziet u dat exemplaren van Temperatuur:

  • Niet correct worden weergegeven als tekenreeksen.
  • Kan niet goed worden gecontroleerd op gelijkwaardigheid.
  • Kan niet worden vergeleken.

Deze drie problemen kunnen worden opgelost door interfaces voor de klasse te implementeren.

IFormattable implementeren

De eerste interface die moet worden geïmplementeerd voor de temperatuurklasse is System.IFormattable. Met deze interface kunt u een exemplaar van de klasse opmaken als verschillende tekenreeksen. Voor het implementeren van de interface moet de klasse overnemen van System.IFormattable en de ToString() instantiemethode definiëren.

De ToString() instantiemethode moet de volgende handtekening hebben:

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

De handtekening die de interface vereist, wordt vermeld in de referentiedocumentatie.

Voor Temperatuur moet de klasse drie indelingen ondersteunen: C om het exemplaar in Celsius te retourneren, F om deze in Fahrenheit te retourneren en K om deze in Kelvin te retourneren. Voor elke andere indeling moet de methode een System.FormatException gooien.

[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 deze implementatie wordt de methode standaard ingesteld op de instantieschaal voor de indeling en de huidige cultuur bij het opmaken van de numerieke gradenwaarde zelf. De instantiemethoden worden gebruikt To<Scale>() om de graden te converteren, op te maken naar twee decimalen en het juiste gradensymbool toe te voegen aan de tekenreeks.

Wanneer de vereiste handtekening is geïmplementeerd, kan de klasse ook overbelastingen definiëren, zodat het eenvoudiger is om het opgemaakte exemplaar te retourneren.

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

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

De volgende code toont de bijgewerkte definitie voor Temperatuur:

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
}

De uitvoer voor de overbelasting van de methode wordt weergegeven in het volgende blok.

$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

IEquatable implementeren

Nu de temperatuurklasse kan worden opgemaakt voor leesbaarheid, moeten gebruikers kunnen controleren of twee exemplaren van de klasse gelijk zijn. Ter ondersteuning van deze test moet de klasse de System.IEquatable-interface implementeren.

Voor het implementeren van de interface moet de klasse overnemen van System.IEquatable en de Equals() instantiemethode definiëren. De Equals() methode moet de volgende handtekening hebben:

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

De handtekening die de interface vereist, wordt vermeld in de referentiedocumentatie.

Voor Temperatuur moet de klasse alleen ondersteuning bieden voor het vergelijken van twee exemplaren van de klasse. Voor een andere waarde of elk ander type, inclusief $null, moet deze retourneren $false. Bij het vergelijken van twee temperaturen moet de methode beide waarden converteren naar Kelvin, omdat temperaturen gelijk kunnen zijn, zelfs met verschillende schalen.

[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()
}

Wanneer de interfacemethode is geïmplementeerd, is de bijgewerkte definitie voor Temperatuur :

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
}

In het volgende blok ziet u hoe de bijgewerkte klasse zich gedraagt:

$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

IComparable implementeren

De laatste interface die moet worden geïmplementeerd voor de temperatuurklasse is System.IComparable. Wanneer de klasse deze interface implementeert, kunnen gebruikers de -ltoperators , -le-gten -ge operators gebruiken om exemplaren van de klasse te vergelijken.

Voor het implementeren van de interface moet de klasse overnemen van System.IComparable en de Equals() instantiemethode definiëren. De Equals() methode moet de volgende handtekening hebben:

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

De handtekening die de interface vereist, wordt vermeld in de referentiedocumentatie.

Voor Temperatuur moet de klasse alleen ondersteuning bieden voor het vergelijken van twee exemplaren van de klasse. Omdat het onderliggende type voor de eigenschap Degrees , zelfs wanneer deze wordt geconverteerd naar een andere schaal, een drijvendekommagetal is, kan de methode afhankelijk zijn van het onderliggende type voor de werkelijke vergelijking.

[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())
}

De uiteindelijke definitie voor de temperatuurklasse is:

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
}

Met de volledige definitie kunnen gebruikers exemplaren van de klasse in PowerShell opmaken en vergelijken, zoals elk ingebouwd type.

$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

Voorbeeld 3: Overnemen van een algemene basisklasse

In dit voorbeeld ziet u hoe u kunt afleiden van een algemene klasse zoals System.Collections.Generic.List.

Een ingebouwde klasse gebruiken als de typeparameter

Voer het volgende codeblok uit. Het laat zien hoe een nieuwe klasse kan overnemen van een algemeen type, zolang de parameter van het type al is gedefinieerd tijdens het parseren.

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

Een aangepaste klasse gebruiken als de typeparameter

Het volgende codeblok definieert eerst een nieuwe klasse, ExampleItem, met één exemplaareigenschap en de ToString() methode. Vervolgens wordt de klasse ExampleItemList gedefinieerd die wordt overgenomen van de basisklasse System.Collections.Generic.List met ExampleItem als de typeparameter.

Kopieer het hele codeblok en voer het uit als één instructie.

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

Als u het hele codeblok uitvoert, treedt er een fout op omdat PowerShell de ExampleItem-klasse nog niet in de runtime heeft geladen. U kunt de klassenaam nog niet gebruiken als de typeparameter voor de basisklasse System.Collections.Generic.List .

Voer de volgende codeblokken uit in de volgorde waarin ze zijn gedefinieerd.

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

Deze keer genereert PowerShell geen fouten. Beide klassen zijn nu gedefinieerd. Voer het volgende codeblok uit om het gedrag van de nieuwe klasse weer te geven.

$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

Een algemeen afleiden met een aangepaste typeparameter in een module

De volgende codeblokken laten zien hoe u een klasse kunt definiëren die wordt overgenomen van een algemene basisklasse die gebruikmaakt van een aangepast type voor de typeparameter.

Sla het volgende codeblok op als GenericExample.psd1.

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

Sla het volgende codeblok op als 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))"
    }
}

Sla het volgende codeblok op als 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()

Tip

De hoofdmodule voegt de aangepaste typen toe aan de typeversnellers van PowerShell. Met dit patroon kunnen modulegebruikers onmiddellijk toegang krijgen tot IntelliSense en automatisch aanvullen voor de aangepaste typen zonder eerst de using module instructie te hoeven gebruiken.

Zie de sectie Exporteren met typeversnellers van about_Classes voor meer informatie over dit patroon.

Importeer de module en controleer de uitvoer.

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

De module wordt zonder fouten geladen omdat de klasse InventoryItem is gedefinieerd in een ander modulebestand dan de inventarisklasse . Beide klassen zijn beschikbaar voor modulegebruikers.

Een basisklasse overnemen

Wanneer een klasse wordt overgenomen van een basisklasse, worden de eigenschappen en methoden van de basisklasse overgenomen. De basisklasseconstructors worden niet rechtstreeks overgenomen, maar ze kunnen wel worden aangeroepen.

Wanneer de basisklasse is gedefinieerd in .NET in plaats van PowerShell, moet u er rekening mee houden dat:

  • PowerShell-klassen kunnen niet overnemen van verzegelde klassen.
  • Bij het overnemen van een algemene basisklasse kan de typeparameter voor de algemene klasse niet de afgeleide klasse zijn. Als u de afgeleide klasse gebruikt als de typeparameter een parseringsfout veroorzaakt.

Zie voorbeeld 1 om te zien hoe overname en onderdrukking werkt voor afgeleide klassen.

Afgeleide klasseconstructors

Afgeleide klassen nemen de constructors van de basisklasse niet rechtstreeks over. Als de basisklasse een standaardconstructor definieert en de afgeleide klasse geen constructors definieert, gebruiken nieuwe exemplaren van de afgeleide klasse de standaardconstructor van de basisklasse. Als de basisklasse geen standaardconstructor definieert, moet de afgeleide klasse ten minste één constructor expliciet definiëren.

Afgeleide klasseconstructors kunnen een constructor aanroepen vanuit de basisklasse met het base trefwoord. Als de afgeleide klasse niet expliciet een constructor aanroept van de basisklasse, wordt in plaats daarvan de standaardconstructor voor de basisklasse aangeroepen.

Als u een niet-standaardbasisconstructor wilt aanroepen, voegt u deze toe : base(<parameters>) na de constructorparameters en vóór het hoofdtekstblok.

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

Bij het definiëren van een constructor die een basisklasseconstructor aanroept, kunnen de parameters een van de volgende items zijn:

  • De variabele van een parameter voor de afgeleide klasseconstructor.
  • Elke statische waarde.
  • Elke expressie die resulteert in een waarde van het parametertype.

In de afbeeldingsklasse in voorbeeld 1 ziet u hoe een afgeleide klasse de basisklasseconstructors kan gebruiken.

Afgeleide klassemethoden

Wanneer een klasse is afgeleid van een basisklasse, neemt deze de methoden van de basisklasse en hun overbelasting over. Eventuele overbelasting van methoden die zijn gedefinieerd op de basisklasse, inclusief verborgen methoden, zijn beschikbaar in de afgeleide klasse.

Een afgeleide klasse kan de overbelasting van een overgenomen methode overschrijven door deze opnieuw te definiëren in de klassedefinitie. Als u de overbelasting wilt overschrijven, moeten de parametertypen hetzelfde zijn als voor de basisklasse. Het uitvoertype voor de overbelasting kan verschillen.

In tegenstelling tot constructors kunnen methoden de : base(<parameters>) syntaxis niet gebruiken om een overbelasting van de basisklasse voor de methode aan te roepen. De opnieuw gedefinieerde overbelasting van de afgeleide klasse vervangt de overbelasting die door de basisklasse is gedefinieerd. Als u de basisklassemethode voor een exemplaar wilt aanroepen, cast u de instantievariabele ($this) naar de basisklasse voordat u de methode aanroept.

In het volgende codefragment ziet u hoe een afgeleide klasse de basisklassemethode kan aanroepen.

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

Zie de afbeeldingsklasse in voorbeeld 1 voor een uitgebreid voorbeeld dat laat zien hoe een afgeleide klasse overgenomen methoden kan overschrijven.

Eigenschappen van afgeleide klassen

Wanneer een klasse is afgeleid van een basisklasse, neemt deze de eigenschappen van de basisklasse over. Alle eigenschappen die zijn gedefinieerd op de basisklasse, inclusief verborgen eigenschappen, zijn beschikbaar in de afgeleide klasse.

Een afgeleide klasse kan een overgenomen eigenschap overschrijven door deze opnieuw te definiëren in de klassedefinitie. De eigenschap van de afgeleide klasse maakt gebruik van het opnieuw gedefinieerde type en de standaardwaarde, indien van toepassing. Als de overgenomen eigenschap een standaardwaarde heeft gedefinieerd en de opnieuw gedefinieerde eigenschap niet, heeft de overgenomen eigenschap geen standaardwaarde.

Als een afgeleide klasse geen statische eigenschap overschrijft, heeft toegang tot de statische eigenschap via de afgeleide klasse toegang tot de statische eigenschap van de basisklasse. Als u de eigenschapswaarde wijzigt via de afgeleide klasse, wordt de waarde op de basisklasse gewijzigd. Elke andere afgeleide klasse die de statische eigenschap niet overschrijft, gebruikt ook de waarde van de eigenschap op de basisklasse. Het bijwerken van de waarde van een overgenomen statische eigenschap in een klasse die de eigenschap niet overschrijft, heeft mogelijk onbedoelde effecten voor klassen die zijn afgeleid van dezelfde basisklasse.

In voorbeeld 1 ziet u hoe afgeleide klassen de eigenschappen van de basisklasse overnemen, uitbreiden en overschrijven.

Afgeleid van generieken

Wanneer een klasse is afgeleid van een algemeen, moet de typeparameter al worden gedefinieerd voordat PowerShell de afgeleide klasse parseert. Als de typeparameter voor het algemene een PowerShell-klasse of -opsomming is die is gedefinieerd in hetzelfde bestand of codeblok, wordt er een fout gegenereerd in PowerShell.

Als u een klasse wilt afleiden van een algemene basisklasse met een aangepast type als de typeparameter, definieert u de klasse of opsomming voor de typeparameter in een ander bestand of een andere module en gebruikt u de instructie om de using module typedefinitie te laden.

Zie voorbeeld 3 voor een voorbeeld van het overnemen van een algemene basisklasse.

Nuttige klassen die moeten worden overgenomen

Er zijn enkele klassen die nuttig kunnen zijn om over te nemen bij het ontwerpen van PowerShell-modules. In deze sectie vindt u een aantal basisklassen en waarvoor een klasse kan worden gebruikt.

  • System.Attribute : leid klassen af om kenmerken te definiëren die kunnen worden gebruikt voor variabelen, parameters, klasse- en opsommingsdefinities en meer.
  • System.Management.Automation.ArgumentTransformationAttribute - Leid klassen af om invoer voor een variabele of parameter te verwerken in een specifiek gegevenstype.
  • System.Management.Automation.ValidateArgumentsAttribute - Leid klassen af om aangepaste validatie toe te passen op variabelen, parameters en klasse-eigenschappen.
  • System.Collections.Generic.List - Klassen afleiden om het maken en beheren van lijsten van een specifiek gegevenstype eenvoudiger te maken.
  • System.Exception : leid klassen af om aangepaste fouten te definiëren.

Interfaces implementeren

Een PowerShell-klasse die een interface implementeert, moet alle leden van die interface implementeren. Als u de leden van de implementatieinterface weglaat, treedt er een parstijdfout op in het script.

Notitie

PowerShell biedt geen ondersteuning voor het declareren van nieuwe interfaces in PowerShell-script. In plaats daarvan moeten interfaces worden gedeclareerd in .NET-code en worden toegevoegd aan de sessie met de Add-Type cmdlet of de using assembly instructie.

Wanneer een klasse een interface implementeert, kan deze worden gebruikt als elke andere klasse die die interface implementeert. Sommige opdrachten en bewerkingen beperken hun ondersteunde typen tot klassen die een specifieke interface implementeren.

Zie voorbeeld 2 als u een voorbeeld van een implementatie van interfaces wilt bekijken.

Nuttige interfaces om te implementeren

Er zijn enkele interfaceklassen die nuttig kunnen zijn om over te nemen bij het ontwerpen van PowerShell-modules. In deze sectie vindt u een aantal basisklassen en waarvoor een klasse kan worden gebruikt.

  • System.IEquatable - Met deze interface kunnen gebruikers twee exemplaren van de klasse vergelijken. Wanneer een klasse deze interface niet implementeert, controleert PowerShell op gelijkwaardigheid tussen twee exemplaren met verwijzings gelijkheid. Met andere woorden, een instantie van de klasse is alleen gelijk aan zichzelf, zelfs als de eigenschapswaarden op twee exemplaren hetzelfde zijn.
  • System.IComparable: met deze interface kunnen gebruikers exemplaren van de klasse vergelijken met de -leoperators , -lt-geen -gt vergelijkingsoperatoren. Wanneer een klasse deze interface niet implementeert, veroorzaken deze operators een fout.
  • System.IFormattable : met deze interface kunnen gebruikers exemplaren van de klasse in verschillende tekenreeksen opmaken. Dit is handig voor klassen met meer dan één standaardtekenreeksweergave, zoals budgetitems, bibliografieën en temperaturen.
  • System.IConvertible : met deze interface kunnen gebruikers exemplaren van de klasse converteren naar andere runtimetypen. Dit is handig voor klassen met een onderliggende numerieke waarde of kunnen worden geconverteerd naar een.

Beperkingen

  • PowerShell biedt geen ondersteuning voor het definiëren van interfaces in scriptcode.

    Tijdelijke oplossing: Definieer interfaces in C# en verwijs naar de assembly die de interfaces definieert.

  • PowerShell-klassen kunnen slechts één basisklasse overnemen.

    Tijdelijke oplossing: overname van klassen is transitief. Een afgeleide klasse kan overnemen van een andere afgeleide klasse om de eigenschappen en methoden van een basisklasse op te halen.

  • Bij het overnemen van een algemene klasse of interface moet de typeparameter voor de algemene klasse al zijn gedefinieerd. Een klasse kan zichzelf niet definiëren als de typeparameter voor een klasse of interface.

    Tijdelijke oplossing: Als u wilt afleiden van een algemene basisklasse of interface, definieert u het aangepaste type in een ander .psm1 bestand en gebruikt u de using module instructie om het type te laden. Er is geen tijdelijke oplossing voor een aangepast type om zichzelf te gebruiken als de typeparameter bij het overnemen van een algemeen type.

Zie ook