about_Classes_Inheritance

Kort beskrivning

Beskriver hur du kan definiera klasser som utökar andra typer.

Lång beskrivning

PowerShell-klasser stöder arv, vilket gör att du kan definiera en underordnad klass som återanvänder (ärver), utökar eller ändrar beteendet för en överordnad klass. Klassen vars medlemmar ärvs kallas för basklassen. Klassen som ärver medlemmarna i basklassen kallas för den härledda klassen.

PowerShell stöder endast enskilt arv. En klass kan bara ärva från en enda klass. Arv är dock transitivt, vilket gör att du kan definiera en arvshierarki för en uppsättning typer. Med andra ord kan typ D ärva från typ C, som ärver från typ B, som ärver från basklasstypen A. Eftersom arv är transitivt är medlemmarna av typ A tillgängliga för typ D.

Härledda klasser ärver inte alla medlemmar i basklassen. Följande medlemmar ärvs inte:

  • Statiska konstruktorer som initierar statiska data för en klass.
  • Instanskonstruktorer som du anropar för att skapa en ny instans av klassen. Varje klass måste definiera sina egna konstruktorer.

Du kan utöka en klass genom att skapa en ny klass som härleds från en befintlig klass. Den härledda klassen ärver egenskaperna och metoderna för basklassen. Du kan lägga till eller åsidosätta basklassmedlemmarna efter behov.

Klasser kan också ärva från gränssnitt som definierar ett kontrakt. En klass som ärver från ett gränssnitt måste implementera det kontraktet. När den gör det kan klassen användas som alla andra klasser som implementerar gränssnittet. Om en klass ärver från ett gränssnitt men inte implementerar gränssnittet genererar PowerShell ett parsningsfel för klassen.

Vissa PowerShell-operatorer är beroende av en klass som implementerar ett specifikt gränssnitt. Operatorn -eq söker till exempel bara efter referensjämlikhet om inte klassen implementerar gränssnittet System.IEquatable . Operatorerna , , och -gt fungerar bara på klasser som implementerar gränssnittet System.IComparable. -ge-lt-le

En härledd klass använder syntaxen : för att utöka en basklass eller implementera gränssnitt. Den härledda klassen ska alltid stå längst till vänster i klassdeklarationen.

Det här exemplet visar den grundläggande arvssyntaxen för PowerShell-klassen.

Class Derived : Base {...}

Det här exemplet visar arv med en gränssnittsdeklaration som kommer efter basklassen.

Class Derived : Base, Interface {...}

Syntax

Klassarv använder följande syntaxer:

En radsyntax

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

Till exempel:

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

Syntax för flera ledningar

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

Till exempel:

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

Exempel

Exempel 1 – Ärva och åsidosätta från en basklass

I följande exempel visas beteendet för ärvda egenskaper med och utan att åsidosätta. Kör kodblocken i ordning efter att ha läst beskrivningen.

Definiera basklassen

Det första kodblocket definierar PublishedWork som en basklass. Den har två statiska egenskaper, List och Artists. Därefter definierar den den statiska RegisterWork() metoden för att lägga till verk i egenskapen static List och konstnärerna till egenskapen Artists och skriver ett meddelande för varje ny post i listorna.

Klassen definierar tre instansegenskaper som beskriver ett publicerat arbete. Slutligen definierar den instansmetoderna Register() och 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)" }
}

Definiera en härledd klass utan åsidosättningar

Den första härledda klassen är Album. Den åsidosätter inte några egenskaper eller metoder. Den lägger till en ny instansegenskap, Genrer, som inte finns i basklassen.

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

Följande kodblock visar beteendet för den härledda albumklassen . Först anger den $VerbosePreference så att meddelandena från klassmetoderna genererar till konsolen. Den skapar tre instanser av klassen, visar dem i en tabell och registrerar dem sedan med den ärvda statiska RegisterWork() metoden. Den anropar sedan samma statiska metod i basklassen direkt.

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

Observera att även om klassen Album inte definierade något värde för Kategori eller några konstruktorer definierades egenskapen av standardkonstruktorn för basklassen.

I de utförliga meddelandena rapporterar det andra anropet RegisterWork() till metoden att verken och konstnärerna redan är registrerade. Även om det första anropet till RegisterWork() var för den härledda albumklassen , använde den den ärvda statiska metoden från basklassen PublishedWork . Den metoden uppdaterade egenskaperna statisk lista och artist i basklassen, som den härledda klassen inte åsidosätter.

Nästa kodblock rensar registret och anropar Register() instansmetoden på albumobjekten.

[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

Instansmetoden på albumobjekten har samma effekt som att anropa den statiska metoden för den härledda eller basklassen.

I följande kodblock jämförs de statiska egenskaperna för basklassen och den härledda klassen, vilket visar att de är desamma.

[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

Definiera en härledd klass med åsidosättningar

Nästa kodblock definierar klassen Illustration som ärver från basklassen PublishedWork . Den nya klassen utökar basklassen genom att definiera egenskapen Medium-instans med standardvärdet Unknown.

Till skillnad från den härledda albumklassen åsidosätter Illustration följande egenskaper och metoder:

  • Den åsidosätter egenskapen static Artists . Definitionen är densamma, men klassen Illustration deklarerar den direkt.
  • Den åsidosätter egenskapen Kategoriinstans och anger standardvärdet till Illustrations.
  • Den åsidosätter ToString() instansmetoden så att strängrepresentationen av en bild innehåller det medium som den skapades med.

Klassen definierar också den statiska RegisterIllustration() metoden för att först anropa basklassmetoden RegisterWork() och sedan lägga till artisten i den åsidosättna statiska egenskapen Artists i den härledda klassen.

Slutligen åsidosätter klassen alla tre konstruktorerna:

  1. Standardkonstruktorn är tom förutom ett utförligt meddelande som anger att den har skapat en bild.
  2. Nästa konstruktor tar två strängvärden för namnet och konstnären som skapade bilden. I stället för att implementera logiken för att ange egenskaperna Namn och Artist anropar konstruktorn lämplig konstruktor från basklassen.
  3. Den sista konstruktorn tar tre strängvärden för bildens namn, artist och medium. Båda konstruktörerna skriver ett utförligt meddelande som anger att de har skapat en bild.
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"
    }
}

Följande kodblock visar beteendet för den härledda klassen Illustration . Den skapar tre instanser av klassen, visar dem i en tabell och registrerar dem sedan med den ärvda statiska RegisterWork() metoden. Den anropar sedan samma statiska metod i basklassen direkt. Slutligen skriver den meddelanden som visar listan över registrerade artister för basklassen och den härledda klassen.

$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 utförliga meddelandena från att skapa instanserna visar att:

  • När du skapade den första instansen anropades standardkonstruktorn för basklassen före standardkonstruktorn för den härledda klassen.
  • När den andra instansen skapades anropades den explicit ärvda konstruktorn för basklassen före konstruktorn för den härledda klassen.
  • När du skapade den tredje instansen anropades standardkonstruktorn för basklassen före konstruktorn för den härledda klassen.

De utförliga meddelandena RegisterWork() från metoden anger att verken och konstnärerna redan var registrerade. Det beror på att RegisterIllustration() metoden kallas RegisterWork() för metoden internt.

Men när du jämför värdet för egenskapen static Artist för både basklassen och den härledda klassen är värdena olika. Egenskapen Artists för den härledda klassen innehåller endast illustratörer, inte albumartister. Omdefiniering av egenskapen Artist i den härledda klassen hindrar klassen från att returnera den statiska egenskapen i basklassen.

Det sista kodblocket ToString() anropar metoden för posterna i egenskapen static List i basklassen.

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

Albuminstanserna returnerar bara namnet och artisten i strängen. Illustrationsinstanserna inkluderade också mediet inom parenteser, eftersom den klassen överskrör ToString() metoden.

Exempel 2 – Implementera gränssnitt

I följande exempel visas hur en klass kan implementera ett eller flera gränssnitt. Exemplet utökar definitionen av en temperaturklass för att stödja fler åtgärder och beteenden.

Inledande klassdefinition

Innan du implementerar några gränssnitt definieras klassen Temperature med två egenskaper, Grader och Skala. Den definierar konstruktorer och tre instansmetoder för att returnera instansen som grader av en viss skala.

Klassen definierar tillgängliga skalor med TemperatureScale-uppräkning .

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
}

I den här grundläggande implementeringen finns det dock några begränsningar som visas i följande exempelutdata:

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

Utdata visar att temperaturinstanser:

  • Visa inte korrekt som strängar.
  • Det går inte att kontrollera korrekt för likvärdighet.
  • Det går inte att jämföra.

Dessa tre problem kan åtgärdas genom att implementera gränssnitt för klassen.

Implementera IFormattable

Det första gränssnittet som ska implementeras för klassen Temperature är System.IFormattable. Det här gränssnittet möjliggör formatering av en instans av klassen som olika strängar. För att implementera gränssnittet måste klassen ärva från System.IFormattable och definiera ToString() instansmetoden.

Instansmetoden ToString() måste ha följande signatur:

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

Signaturen som krävs för gränssnittet visas i referensdokumentationen.

För Temperatur bör klassen ha stöd för tre format: C att returnera instansen i Celsius, F att returnera den i Fahrenheit och K att returnera den i Kelvin. För andra format bör metoden utlösa en 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'"
    )
}

I den här implementeringen är metoden standard för instansskalan för format och den aktuella kulturen när du formaterar själva värdet för numerisk grad. Den använder instansmetoderna To<Scale>() för att konvertera graderna, formaterar dem till två decimaler och lägger till lämplig gradsymbol i strängen.

Med den nödvändiga signaturen implementerad kan klassen också definiera överlagringar för att göra det enklare att returnera den formaterade instansen.

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

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

Följande kod visar den uppdaterade definitionen för Temperatur:

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
}

Utdata för metodöverbelastningar visas i följande block.

$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

Implementera IEquatable

Nu när klassen Temperature kan formateras för läsbarhet måste användarna kunna kontrollera om två instanser av klassen är lika. För att stödja det här testet måste klassen implementera System.IEquatable-gränssnittet .

För att implementera gränssnittet måste klassen ärva från System.IEquatable och definiera Equals() instansmetoden. Metoden Equals() måste ha följande signatur:

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

Signaturen som krävs för gränssnittet visas i referensdokumentationen.

För Temperatur bör klassen endast ha stöd för att jämföra två instanser av klassen. För andra värden eller typer, inklusive $null, bör det returnera $false. Vid jämförelse av två temperaturer bör metoden konvertera båda värdena till Kelvin, eftersom temperaturerna kan vara likvärdiga även med olika skalor.

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

Med den implementerade gränssnittsmetoden är den uppdaterade definitionen för Temperatur :

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
}

Följande block visar hur den uppdaterade klassen beter sig:

$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

Implementera IComparable

Det sista gränssnittet som ska implementeras för klassen Temperature är System.IComparable. När klassen implementerar det här gränssnittet kan användarna använda -ltoperatorerna , -le, -gtoch för -ge att jämföra instanser av klassen.

För att implementera gränssnittet måste klassen ärva från System.IComparable och definiera Equals() instansmetoden. Metoden Equals() måste ha följande signatur:

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

Signaturen som krävs för gränssnittet visas i referensdokumentationen.

För Temperatur bör klassen endast ha stöd för att jämföra två instanser av klassen. Eftersom den underliggande typen för egenskapen Grader , även om den konverteras till en annan skala, är ett flyttalsnummer, kan metoden förlita sig på den underliggande typen för den faktiska jämförelsen.

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

Den slutliga definitionen för klassen Temperature är:

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
}

Med den fullständiga definitionen kan användare formatera och jämföra instanser av klassen i PowerShell som alla inbyggda typer.

$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

Exempel 3 – Ärv från en allmän basklass

Det här exemplet visar hur du kan härleda från en allmän klass som System.Collections.Generic.List.

Använda en inbyggd klass som typparameter

Kör följande kodblock. Den visar hur en ny klass kan ärva från en allmän typ så länge typparametern redan har definierats vid parsningstid.

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

Använda en anpassad klass som typparameter

Nästa kodblock definierar först en ny klass, ExampleItem, med en enda instansegenskap och ToString() metoden. Sedan definieras klassen ExampleItemList som ärver från basklassen System.Collections.Generic.List med ExampleItem som typparameter.

Kopiera hela kodblocket och kör det som en enda instruktion.

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

Om du kör hela kodblocket uppstår ett fel eftersom PowerShell inte har läst in klassen ExampleItem i körningen ännu. Du kan inte använda klassnamnet som typparameter för basklassen System.Collections.Generic.List ännu.

Kör följande kodblock i den ordning de definieras.

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

Den här gången skapar PowerShell inga fel. Båda klasserna har nu definierats. Kör följande kodblock för att visa beteendet för den nya klassen.

$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

Härleda en generisk med en anpassad typparameter i en modul

Följande kodblock visar hur du kan definiera en klass som ärver från en allmän basklass som använder en anpassad typ för typparametern.

Spara följande kodblock som GenericExample.psd1.

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

Spara följande kodblock som 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))"
    }
}

Spara följande kodblock som 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()

Dricks

Rotmodulen lägger till de anpassade typerna i PowerShells typacceleratorer. Det här mönstret gör det möjligt för modulanvändare att omedelbart komma åt IntelliSense och komplettera automatiskt för de anpassade typerna utan att behöva använda instruktionen using module först.

Mer information om det här mönstret finns i avsnittet "Exportera med typacceleratorer" i about_Classes.

Importera modulen och verifiera utdata.

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

Modulen läses in utan fel eftersom klassen InventoryItem definieras i en annan modulfil än klassen Inventory . Båda klasserna är tillgängliga för modulanvändare.

Ärver en basklass

När en klass ärver från en basklass ärver den basklassens egenskaper och metoder. Den ärver inte basklasskonstruktorerna direkt, men den kan anropa dem.

Observera att när basklassen definieras i .NET i stället för PowerShell:

  • PowerShell-klasser kan inte ärva från förseglade klasser.
  • När du ärver från en allmän basklass kan typparametern för den generiska klassen inte vara den härledda klassen. Om du använder den härledda klassen som typparameter genereras ett parsfel.

Information om hur arv och åsidosättande fungerar för härledda klasser finns i Exempel 1.

Härledda klasskonstruktorer

Härledda klasser ärver inte konstruktorerna i basklassen direkt. Om basklassen definierar en standardkonstruktor och den härledda klassen inte definierar några konstruktorer använder nya instanser av den härledda klassen standardkonstruktorn för basklassen. Om basklassen inte definierar en standardkonstruktor måste den härledda klassen uttryckligen definiera minst en konstruktor.

Härledda klasskonstruktorer kan anropa en konstruktor från basklassen med nyckelordet base . Om den härledda klassen inte uttryckligen anropar en konstruktor från basklassen anropas standardkonstruktorn för basklassen i stället.

Om du vill anropa en nondefault-baskonstruktor lägger du till : base(<parameters>) efter konstruktorparametrarna och före brödtextblocket.

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

När du definierar en konstruktor som anropar en basklasskonstruktor kan parametrarna vara något av följande objekt:

  • Variabeln för valfri parameter i den härledda klasskonstruktorn.
  • Alla statiska värden.
  • Alla uttryck som utvärderas till ett värde av parametertypen.

Klassen Illustration i exempel 1 visar hur en härledd klass kan använda basklasskonstruktorerna.

Härledda klassmetoder

När en klass härleds från en basklass ärver den metoderna för basklassen och deras överlagringar. Alla metodöverlagringar som definierats i basklassen, inklusive dolda metoder, är tillgängliga för den härledda klassen.

En härledd klass kan åsidosätta en ärvd metodöverbelastning genom att omdefiniera den i klassdefinitionen. Om du vill åsidosätta överlagringen måste parametertyperna vara samma som för basklassen. Utdatatypen för överlagringen kan vara annorlunda.

Till skillnad från konstruktorer kan metoder inte använda syntaxen : base(<parameters>) för att anropa en basklassöverlagring för metoden. Den omdefinierade överbelastningen på den härledda klassen ersätter helt den överlagring som definieras av basklassen. Om du vill anropa basklassmetoden för en instans omvandlar du instansvariabeln ($this) till basklassen innan du anropar metoden.

Följande kodfragment visar hur en härledd klass kan anropa basklassmetoden.

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

Ett utökat exempel som visar hur en härledd klass kan åsidosätta ärvda metoder finns i klassen Illustration i Exempel 1.

Egenskaper för härledd klass

När en klass härleds från en basklass ärver den egenskaperna för basklassen. Alla egenskaper som definierats i basklassen, inklusive dolda egenskaper, är tillgängliga för den härledda klassen.

En härledd klass kan åsidosätta en ärvd egenskap genom att omdefiniera den i klassdefinitionen. Egenskapen för den härledda klassen använder den omdefinierade typen och standardvärdet, om det finns. Om den ärvda egenskapen definierade ett standardvärde och den omdefinierade egenskapen inte gör det, har den ärvda egenskapen inget standardvärde.

Om en härledd klass inte åsidosätter en statisk egenskap får åtkomst till den statiska egenskapen via den härledda klassen åtkomst till basklassens statiska egenskap. Om du ändrar egenskapsvärdet via den härledda klassen ändras värdet för basklassen. Alla andra härledda klasser som inte åsidosätter den statiska egenskapen använder också värdet för egenskapen i basklassen. Att uppdatera värdet för en ärvd statisk egenskap i en klass som inte åsidosätter egenskapen kan ha oavsiktliga effekter för klasser som härleds från samma basklass.

Exempel 1 visar hur härledda klasser som ärver, utökar och åsidosätter basklassegenskaperna.

Härleda från generiska läkemedel

När en klass härleds från en generisk måste typparametern redan definieras innan PowerShell parsar den härledda klassen. Om typparametern för den generiska filen är en PowerShell-klass eller uppräkning som definierats i samma fil eller kodblock genererar PowerShell ett fel.

Om du vill härleda en klass från en allmän basklass med en anpassad typ som typparameter, definierar du klassen eller uppräkningen för typparametern i en annan fil eller modul och använder instruktionen using module för att läsa in typdefinitionen.

Ett exempel som visar hur du ärver från en allmän basklass finns i Exempel 3.

Användbara klasser att ärva

Det finns några klasser som kan vara användbara att ärva när du redigerar PowerShell-moduler. I det här avsnittet visas några basklasser och vad en klass som härleds från dem kan användas för.

  • System.Attribute – Härled klasser för att definiera attribut som kan användas för variabler, parametrar, klass- och uppräkningsdefinitioner med mera.
  • System.Management.Automation.ArgumentTransformationAttribute – Härled klasser för att hantera konvertering av indata för en variabel eller parameter till en specifik datatyp.
  • System.Management.Automation.ValidateArgumentsAttribute – Härled klasser för att tillämpa anpassad validering på variabler, parametrar och klassegenskaper.
  • System.Collections.Generic.List – Härled klasser för att göra det enklare att skapa och hantera listor av en viss datatyp.
  • System.Exception – Härled klasser för att definiera anpassade fel.

Implementera gränssnitt

En PowerShell-klass som implementerar ett gränssnitt måste implementera alla medlemmar i gränssnittet. Om du utelämnar implementeringsgränssnittets medlemmar uppstår ett parse-time-fel i skriptet.

Kommentar

PowerShell har inte stöd för att deklarera nya gränssnitt i PowerShell-skript. I stället måste gränssnitt deklareras i .NET-kod och läggas till i sessionen med cmdleten Add-Type eller -instruktionen using assembly .

När en klass implementerar ett gränssnitt kan den användas som vilken annan klass som helst som implementerar gränssnittet. Vissa kommandon och åtgärder begränsar deras typer som stöds till klasser som implementerar ett specifikt gränssnitt.

Information om hur du granskar en exempelimplementering av gränssnitt finns i Exempel 2.

Användbara gränssnitt att implementera

Det finns några gränssnittsklasser som kan vara användbara att ärva när du redigerar PowerShell-moduler. I det här avsnittet visas några basklasser och vad en klass som härleds från dem kan användas för.

  • System.IEquatable – Med det här gränssnittet kan användarna jämföra två instanser av klassen. När en klass inte implementerar det här gränssnittet söker PowerShell efter likvärdighet mellan två instanser med hjälp av referensjämlikhet. Med andra ord är en instans av klassen bara lika med sig själv, även om egenskapsvärdena på två instanser är desamma.
  • System.IComparable – Med det här gränssnittet kan användarna jämföra instanser av klassen med -leoperatorerna , -lt, -geoch -gt jämförelse. När en klass inte implementerar det här gränssnittet skapar dessa operatorer ett fel.
  • System.IFormattable – Med det här gränssnittet kan användare formatera instanser av klassen i olika strängar. Detta är användbart för klasser som har mer än en standardsträngrepresentation, till exempel budgetobjekt, litteraturförteckningar och temperaturer.
  • System.IConvertible – Det här gränssnittet gör det möjligt för användare att konvertera instanser av klassen till andra körningstyper. Detta är användbart för klasser som har ett underliggande numeriskt värde eller kan konverteras till ett.

Begränsningar

  • PowerShell stöder inte definition av gränssnitt i skriptkod.

    Lösning: Definiera gränssnitt i C# och referera till sammansättningen som definierar gränssnitten.

  • PowerShell-klasser kan bara ärva från en basklass.

    Lösning: Klassarv är transitivt. En härledd klass kan ärva från en annan härledd klass för att hämta egenskaperna och metoderna för en basklass.

  • När du ärver från en allmän klass eller ett gränssnitt måste typparametern för det generiska redan ha definierats. En klass kan inte definiera sig själv som typparameter för en klass eller ett gränssnitt.

    Lösning: Om du vill härleda från en allmän basklass eller ett allmänt basgränssnitt definierar du den anpassade typen i en annan .psm1 fil och använder instruktionen using module för att läsa in typen. Det finns ingen lösning för en anpassad typ att använda sig själv som typparameter när du ärver från en generisk.

Se även