Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Krótki opis
Opisuje sposób definiowania klas, które rozszerzają inne typy.
Długi opis
Klasy programu PowerShell obsługują dziedziczenia, co umożliwia zdefiniowanie klasy podrzędnej, która ponownie (dziedziczy), rozszerza lub modyfikuje zachowanie klasy nadrzędnej. Klasa, której składowe są dziedziczone, jest nazywana klasą bazową . Klasa dziedziczące składowe klasy bazowej jest nazywana klasą pochodną .
Program PowerShell obsługuje tylko pojedyncze dziedziczenie. Klasa może dziedziczyć tylko z jednej klasy. Jednak dziedziczenie jest przechodnie, co umożliwia zdefiniowanie hierarchii dziedziczenia dla zestawu typów. Innymi słowy, typ D może dziedziczyć z typu C, który dziedziczy z typu B, który dziedziczy z typu klasy bazowej A. Ponieważ dziedziczenie jest przechodnie, elementy członkowskie typu A są dostępne do wpisywania D.
Klasy pochodne nie dziedziczą wszystkich składowych klasy bazowej. Następujące elementy członkowskie nie są dziedziczone:
- Konstruktory statyczne, które inicjują dane statyczne klasy.
- Konstruktory wystąpień, które są wywoływane w celu utworzenia nowego wystąpienia klasy. Każda klasa musi definiować własne konstruktory.
Klasę można rozszerzyć, tworząc nową klasę pochodzącą z istniejącej klasy. Klasa pochodna dziedziczy właściwości i metody klasy bazowej. Możesz dodać lub zastąpić składowe klasy bazowej zgodnie z wymaganiami.
Klasy mogą również dziedziczyć z interfejsów, które definiują kontrakt. Klasa dziedziczona z interfejsu musi implementować ten kontrakt. W takim przypadku klasa może być użyteczna jak każda inna klasa implementujący ten interfejs. Jeśli klasa dziedziczy z interfejsu, ale nie implementuje interfejsu, program PowerShell zgłasza błąd analizy dla klasy.
Niektóre operatory programu PowerShell zależą od klasy implementowania określonego interfejsu.
Na przykład operator -eq sprawdza równość odwołania, chyba że klasa implementuje interfejs System.IEquatable. Operatory -le, -lt, -gei -gt działają tylko na klasach implementujących interfejs System.IComparable.
Klasa pochodna używa składni : do rozszerzania klasy bazowej lub implementowania interfejsów. Klasa pochodna powinna zawsze znajdować się w lewej części deklaracji klasy.
W tym przykładzie przedstawiono podstawową składnię dziedziczenia klas programu PowerShell.
class Derived : Base {...}
W tym przykładzie pokazano dziedziczenie z deklaracją interfejsu przychodzącą po klasie bazowej.
class Derived : Base, Interface {...}
Składnia
Dziedziczenie klas używa następujących składni:
Składnia jednego wiersza
class <derived-class-name> : <base-class-or-interface-name>[, <interface-name>...] {
<derived-class-body>
}
Na przykład:
# Base class only
class Derived : Base {...}
# Interface only
class Derived : System.IComparable {...}
# Base class and interface
class Derived : Base, System.IComparable {...}
Składnia wielowierszowa
class <derived-class-name> : <base-class-or-interface-name>[,
<interface-name>...] {
<derived-class-body>
}
Na przykład:
class Derived : Base,
System.IComparable,
System.IFormattable,
System.IConvertible {
# Derived class definition
}
Przykłady
Przykład 1 — dziedziczenie i zastępowanie z klasy bazowej
W poniższym przykładzie pokazano zachowanie dziedziczych właściwości z przesłonięciami i bez ich zastępowania. Uruchom bloki kodu w kolejności po przeczytaniu ich opisu.
Definiowanie klasy bazowej
Pierwszy blok kodu definiuje PublishedWork jako klasę bazową. Ma dwie właściwości statyczne, List i Artists. Następnie definiuje statyczną metodę RegisterWork(), aby dodać prace do właściwości statycznej listy oraz artystów do właściwości Artists, pisząc komunikat dla każdego nowego wpisu na listach.
Klasa definiuje trzy właściwości wystąpienia, które opisują opublikowaną pracę.
Na koniec definiuje metody Register() i wystąpienia 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)" }
}
Definiowanie klasy pochodnej bez przesłonięć
Pierwsza klasa pochodna to Album. Nie zastępuje żadnych właściwości ani metod. Dodaje nową właściwość wystąpienia, Gatunek, która nie istnieje w klasie bazowej.
class Album : PublishedWork {
[string[]] $Genres = @()
}
Poniższy blok kodu przedstawia zachowanie klasy albumu pochodnego.
Najpierw ustawia $VerbosePreference tak, aby komunikaty z metod klasy emitować do konsoli. Tworzy trzy wystąpienia klasy, pokazuje je w tabeli, a następnie rejestruje je przy użyciu odziedziczonej metody RegisterWork() statycznej. Następnie wywołuje tę samą metodę statyczną bezpośrednio w klasie bazowej.
$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.
Zwróć uwagę, że mimo że klasa albumu nie zdefiniowała wartości Category ani żadnych konstruktorów, właściwość została zdefiniowana przez domyślny konstruktor klasy bazowej.
W pełnej wiadomości drugie wywołanie metody RegisterWork() zgłasza, że dzieła i artyści są już zarejestrowane. Mimo że pierwsze wywołanie RegisterWork() było przeznaczone dla klasy albumów pochodnych, użyto odziedziczonej metody statycznej z klasy base PublishedWork. Ta metoda zaktualizowała właściwości static List i Artist w klasie bazowej, których klasa pochodna nie przesłoniła.
Następny blok kodu czyści rejestr i wywołuje metodę wystąpienia Register() w obiektach albumu.
[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
Metoda wystąpienia w obiektach albumu ma taki sam wpływ, jak wywoływanie metody statycznej w klasie pochodnej lub bazowej.
Poniższy blok kodu porównuje właściwości statyczne dla klasy bazowej i klasy pochodnej, pokazując, że są one takie same.
[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
Definiowanie klasy pochodnej za pomocą przesłonięć
Następny blok kodu definiuje klasę Illustration dziedziczącą z podstawowej klasy PublishedWork. Nowa klasa rozszerza klasę bazową, definiując właściwość wystąpienia Średni z wartością domyślną Unknown.
W przeciwieństwie do klasy albumu pochodnego, Ilustracja zastępuje następujące właściwości i metody:
- Zastępuje on statyczną właściwość Artists. Definicja jest taka sama, ale klasa Ilustracja deklaruje ją bezpośrednio.
- Zastępuje właściwość wystąpienia kategorii , ustawiając wartość domyślną na
Illustrations. - Zastępuje ona metodę wystąpienia
ToString(), tak aby reprezentacja ciągu ilustracji zawierała nośnik, za pomocą którego została utworzona.
Klasa definiuje również metodę statyczną RegisterIllustration(), aby najpierw wywołać metodę RegisterWork() klasy bazowej, a następnie dodać artystę do zastąpionej właściwości Artists statycznej klasy pochodnej.
Na koniec klasa zastępuje wszystkie trzy konstruktory:
- Domyślny konstruktor jest pusty, z wyjątkiem pełnej wiadomości wskazującej, że utworzył ilustrację.
- Następny konstruktor przyjmuje dwie wartości ciągu dla nazwy i artysty, które utworzyły ilustrację. Zamiast implementowania logiki ustawiania właściwości Name i Artist, konstruktor wywołuje odpowiedni konstruktor z klasy bazowej.
- Ostatni konstruktor przyjmuje trzy wartości ciągu dla nazwy, artysty i medium ilustracji. Oba konstruktory zapisują pełny komunikat wskazujący, że utworzyli ilustrację.
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"
}
}
Poniższy blok kodu przedstawia zachowanie pochodnej klasy Ilustracja. Tworzy trzy wystąpienia klasy, pokazuje je w tabeli, a następnie rejestruje je przy użyciu odziedziczonej metody RegisterWork() statycznej. Następnie wywołuje tę samą metodę statyczną bezpośrednio w klasie bazowej. Na koniec zapisuje komunikaty przedstawiające listę zarejestrowanych artystów dla klasy bazowej i klasy pochodnej.
$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
Pełne komunikaty z tworzenia wystąpień pokazują, że:
- Podczas tworzenia pierwszego wystąpienia konstruktor domyślny klasy bazowej został wywołany przed konstruktorem domyślnym klasy pochodnej.
- Podczas tworzenia drugiego wystąpienia konstruktor jawnie dziedziczony był wywoływany dla klasy bazowej przed konstruktorem klasy pochodnej.
- Podczas tworzenia trzeciego wystąpienia konstruktor domyślny klasy bazowej został wywołany przed konstruktorem klasy pochodnej.
Pełne komunikaty z metody RegisterWork() wskazują, że dzieła i artyści zostali już zarejestrowani. Wynika to z faktu, że metoda RegisterIllustration() nazywana metodą RegisterWork() wewnętrznie.
Jednak podczas porównywania wartości właściwości static Artist zarówno dla klasy bazowej, jak i klasy pochodnej, wartości są różne. Obiekt Artists klasy pochodnej obejmuje tylko ilustratorów, a nie artystów albumów. Ponowne zdefiniowanie właściwości Artist w klasie pochodnej uniemożliwia klasie zwracanie właściwości statycznej w klasie bazowej.
Końcowy blok kodu wywołuje metodę ToString() we wpisach właściwości statycznej listy w klasie bazowej.
[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)
Wystąpienia albumu zwracają tylko nazwę i artystę w swoim ciągu. Wystąpienie ilustracji zawiera również medium w nawiasach, ponieważ ta klasa zastępuje metodę ToString().
Przykład 2 — Implementowanie interfejsów
W poniższym przykładzie pokazano, jak klasa może zaimplementować jeden lub więcej interfejsów. Przykład rozszerza definicję klasy Temperature w celu obsługi większej liczby operacji i zachowań.
Początkowa definicja klasy
Przed zaimplementowaniem jakichkolwiek interfejsów klasa temperatury jest zdefiniowana z dwiema właściwościami, Degrees i Scale. Definiuje konstruktory i trzy metody wystąpienia do zwracania wystąpienia jako stopni określonej skali.
Klasa definiuje dostępne skalowanie przy użyciu wyliczenia 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
}
Jednak w tej podstawowej implementacji istnieje kilka ograniczeń, jak pokazano w następujących przykładowych danych wyjściowych:
$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.
Dane wyjściowe pokazują, że wystąpienia Temperature:
- Nie wyświetlaj poprawnie jako ciągów.
- Nie można sprawdzić prawidłowo pod kątem równoważności.
- Nie można porównać.
Te trzy problemy można rozwiązać, implementując interfejsy dla klasy.
Implementowanie tabeli IFormattable
Pierwszym interfejsem implementowania dla klasy Temperature jest System.IFormattable. Ten interfejs umożliwia formatowanie wystąpienia klasy jako różnych ciągów. Aby zaimplementować interfejs, klasa musi dziedziczyć z System.IFormattable i zdefiniować metodę wystąpienia ToString().
Metoda wystąpienia ToString() musi mieć następujący podpis:
[string] ToString(
[string]$Format,
[System.IFormatProvider]$FormatProvider
) {
# Implementation
}
Podpis wymagany przez interfejs znajduje się w dokumentacji referencyjnej .
W przypadku temperatureklasa powinna obsługiwać trzy formaty: C zwrócić wystąpienie w stopniach Celsjusza, F zwrócić je w Fahrenheita i K, aby zwrócić je w Kelvin. W przypadku dowolnego innego formatu metoda powinna zgłosić 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'"
)
}
W tej implementacji metoda jest domyślnie skalowana dla wystąpienia dla formatu i bieżącej kultury podczas formatowania samej wartości stopnia liczbowego. Używa metod wystąpienia To<Scale>() do konwertowania stopni, formatuje je do dwóch miejsc dziesiętnych i dołącza odpowiedni symbol stopnia do ciągu.
Dzięki zaimplementowaniu wymaganego podpisu klasa może również definiować przeciążenia, aby ułatwić zwracanie sformatowanego wystąpienia.
[string] ToString([string]$Format) {
return $this.ToString($Format, $null)
}
[string] ToString() {
return $this.ToString($null, $null)
}
Poniższy kod przedstawia zaktualizowaną definicję Temperature:
class Temperature : System.IFormattable {
[float] $Degrees
[TemperatureScale] $Scale
Temperature() {}
Temperature([float] $Degrees) { $this.Degrees = $Degrees }
Temperature([TemperatureScale] $Scale) { $this.Scale = $Scale }
Temperature([float] $Degrees, [TemperatureScale] $Scale) {
$this.Degrees = $Degrees
$this.Scale = $Scale
}
[float] ToKelvin() {
switch ($this.Scale) {
Celsius { return $this.Degrees + 273.15 }
Fahrenheit { return ($this.Degrees + 459.67) * 5 / 9 }
}
return $this.Degrees
}
[float] ToCelsius() {
switch ($this.Scale) {
Fahrenheit { return ($this.Degrees - 32) * 5 / 9 }
Kelvin { return $this.Degrees - 273.15 }
}
return $this.Degrees
}
[float] ToFahrenheit() {
switch ($this.Scale) {
Celsius { return $this.Degrees * 9 / 5 + 32 }
Kelvin { return $this.Degrees * 9 / 5 - 459.67 }
}
return $this.Degrees
}
[string] ToString(
[string]$Format,
[System.IFormatProvider]$FormatProvider
) {
# If format isn't specified, use the defined scale.
if ([string]::IsNullOrEmpty($Format)) {
$Format = switch ($this.Scale) {
Celsius { 'C' }
Fahrenheit { 'F' }
Kelvin { 'K' }
}
}
# If format provider isn't specified, use the current culture.
if ($null -eq $FormatProvider) {
$FormatProvider = [cultureinfo]::CurrentCulture
}
# Format the temperature.
switch ($Format) {
'C' {
return $this.ToCelsius().ToString('F2', $FormatProvider) + '°C'
}
'F' {
return $this.ToFahrenheit().ToString('F2', $FormatProvider) + '°F'
}
'K' {
return $this.ToKelvin().ToString('F2', $FormatProvider) + '°K'
}
}
# If we get here, the format is invalid.
throw [System.FormatException]::new(
"Unknown format: '$Format'. Valid Formats are 'C', 'F', and 'K'"
)
}
[string] ToString([string]$Format) {
return $this.ToString($Format, $null)
}
[string] ToString() {
return $this.ToString($null, $null)
}
}
enum TemperatureScale {
Celsius = 0
Fahrenheit = 1
Kelvin = 2
}
Dane wyjściowe przeciążeń metody są wyświetlane w poniższym bloku.
$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
Implementowanie elementu IEquatable
Teraz, gdy klasę Temperature można sformatować w celu zapewnienia czytelności, użytkownicy muszą mieć możliwość sprawdzenia, czy dwa wystąpienia klasy są równe. Aby obsługiwać ten test, klasa musi zaimplementować interfejs System.IEquatable.
Aby zaimplementować interfejs, klasa musi dziedziczyć z System.IEquatable i zdefiniować metodę wystąpienia Equals(). Metoda Equals() musi mieć następujący podpis:
[bool] Equals([Object]$Other) {
# Implementation
}
Podpis wymagany przez interfejs znajduje się w dokumentacji referencyjnej .
W przypadku temperatureklasa powinna obsługiwać tylko porównanie dwóch wystąpień klasy. Dla każdej innej wartości lub typu, w tym $null, powinna zwrócić $false. Podczas porównywania dwóch temperatur metoda powinna przekonwertować obie wartości na Kelvin, ponieważ temperatury mogą być równoważne nawet w przypadku różnych skali.
[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()
}
Po zaimplementowaniu metody interfejsu zaktualizowana definicja Temperature to:
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
}
Poniższy blok pokazuje, jak zachowuje się zaktualizowana klasa:
$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
Implementowanie IComparable
Ostatnim interfejsem implementowania dla klasy Temperature jest System.IComparable. Gdy klasa implementuje ten interfejs, użytkownicy mogą używać operatorów -lt, -le, -gti -ge, aby porównać wystąpienia klasy.
Aby zaimplementować interfejs, klasa musi dziedziczyć z System.IComparable i zdefiniować metodę wystąpienia Equals(). Metoda Equals() musi mieć następujący podpis:
[int] CompareTo([Object]$Other) {
# Implementation
}
Podpis wymagany przez interfejs znajduje się w dokumentacji referencyjnej .
W przypadku temperatureklasa powinna obsługiwać tylko porównanie dwóch wystąpień klasy. Ponieważ podstawowy typ właściwości Degrees, nawet w przypadku konwersji na inną skalę, jest liczbą zmiennoprzecinkową, metoda może polegać na typie bazowym dla rzeczywistego porównania.
[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())
}
Ostateczna definicja klasy Temperature to:
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
}
Dzięki pełnej definicji użytkownicy mogą formatować i porównywać wystąpienia klasy w programie PowerShell, podobnie jak w przypadku każdego typu wbudowanego.
$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
Przykład 3 — dziedziczenie z ogólnej klasy bazowej
W tym przykładzie pokazano, jak można pochodzić z typu ogólnego, o ile parametr typu jest już zdefiniowany w czasie analizy.
Używanie wbudowanej klasy jako parametru typu
Uruchom następujący blok kodu. Pokazuje, jak nowa klasa może dziedziczyć z typu ogólnego, o ile parametr typu jest już zdefiniowany w czasie analizy.
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
Używanie klasy niestandardowej jako parametru typu
Następny blok kodu najpierw definiuje nową klasę, ExampleItem, z właściwością pojedynczego wystąpienia i metodą ToString(). Następnie definiuje klasę ExampleItemList dziedziczącą z klasy System.Collections.Generic.List z klasą bazową ExampleItem jako parametr typu.
Skopiuj cały blok kodu i uruchom go jako pojedynczą instrukcję.
class ExampleItem {
[string] $Name
[string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}
ParentContainsErrorRecordException: An error occurred while creating the pipeline.
Uruchomienie całego bloku kodu powoduje wystąpienie błędu, ponieważ program PowerShell nie załadował jeszcze klasy ExampleItem do środowiska uruchomieniowego. Nie można użyć nazwy klasy jako parametru typu dla klasy System.Collections.Generic.List klasy bazowej.
Uruchom następujące bloki kodu w kolejności, w której są zdefiniowane.
class ExampleItem {
[string] $Name
[string] ToString() { return $this.Name }
}
class ExampleItemList : System.Collections.Generic.List[ExampleItem] {}
Tym razem program PowerShell nie zgłasza żadnych błędów. Obie klasy są teraz zdefiniowane. Uruchom następujący blok kodu, aby wyświetlić zachowanie nowej klasy.
$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
Wyprowadzanie ogólnego z parametrem typu niestandardowego w module
Poniższe bloki kodu pokazują, jak można zdefiniować klasę, która dziedziczy z ogólnej klasy bazowej, która używa niestandardowego typu dla parametru typu.
Zapisz następujący blok kodu jako GenericExample.psd1.
@{
RootModule = 'GenericExample.psm1'
ModuleVersion = '0.1.0'
GUID = '2779fa60-0b3b-4236-b592-9060c0661ac2'
}
Zapisz następujący blok kodu jako 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))"
}
}
Zapisz następujący blok kodu jako 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()
Napiwek
Moduł główny dodaje typy niestandardowe do akceleratorów typów programu PowerShell. Ten wzorzec umożliwia użytkownikom modułów natychmiastowe uzyskiwanie dostępu do funkcji IntelliSense i autouzupełnianie dla typów niestandardowych bez konieczności wcześniejszego używania instrukcji using module.
Aby uzyskać więcej informacji na temat tego wzorca, zobacz sekcję "Eksportowanie z akceleratorami typów" about_Classes.
Zaimportuj moduł i sprawdź dane wyjściowe.
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
Moduł ładuje się bez błędów, ponieważ klasa InventoryItem jest zdefiniowana w innym pliku modułu niż klasa Inventory. Obie klasy są dostępne dla użytkowników modułów.
Dziedziczenie klasy bazowej
Gdy klasa dziedziczy z klasy bazowej, dziedziczy właściwości i metody klasy bazowej. Konstruktory klas bazowych nie dziedziczą bezpośrednio, ale mogą je wywoływać.
Jeśli klasa bazowa jest zdefiniowana na platformie .NET, a nie w programie PowerShell, należy pamiętać, że:
- Klasy programu PowerShell nie mogą dziedziczyć z klas zapieczętowanych.
- W przypadku dziedziczenia z ogólnej klasy bazowej parametr typu dla klasy ogólnej nie może być klasą pochodną. Użycie klasy pochodnej jako parametru typu zgłasza błąd analizy.
Aby zobaczyć, jak działa dziedziczenie i zastępowanie dla klas pochodnych, zobacz Przykład 1.
Konstruktory klas pochodnych
Klasy pochodne nie dziedziczą bezpośrednio konstruktorów klasy bazowej. Jeśli klasa bazowa definiuje konstruktor domyślny, a klasa pochodna nie definiuje żadnych konstruktorów, nowe wystąpienia klasy pochodnej używają konstruktora domyślnego klasy bazowej. Jeśli klasa bazowa nie definiuje konstruktora domyślnego, klasa pochodna musi jawnie zdefiniować co najmniej jeden konstruktor.
Konstruktory klasy pochodnej mogą wywoływać konstruktor z klasy bazowej za pomocą słowa kluczowego base. Jeśli klasa pochodna nie wywołuje jawnie konstruktora z klasy bazowej, wywołuje zamiast tego konstruktor domyślny dla klasy bazowej.
Aby wywołać konstruktor podstawowy bez definicji, dodaj : base(<parameters>) po parametrach konstruktora i przed blokiem treści.
class <derived-class> : <base-class> {
<derived-class>(<derived-parameters>) : <base-class>(<base-parameters>) {
# initialization code
}
}
Podczas definiowania konstruktora, który wywołuje konstruktor klasy bazowej, parametry mogą być dowolnym z następujących elementów:
- Zmienna dowolnego parametru w konstruktorze klasy pochodnej.
- Dowolna wartość statyczna.
- Dowolne wyrażenie, które oblicza wartość typu parametru.
Klasa Ilustracja w Przykład 1 pokazuje, jak klasa pochodna może używać konstruktorów klasy bazowej.
Metody klasy pochodnej
Gdy klasa pochodzi z klasy bazowej, dziedziczy metody klasy bazowej i ich przeciążenia. Wszystkie przeciążenia metody zdefiniowane w klasie bazowej, w tym ukryte metody, są dostępne w klasie pochodnej.
Klasa pochodna może zastąpić przeciążenie dziedziczonej metody przez ponowne zdefiniowanie jej w definicji klasy. Aby zastąpić przeciążenie, typy parametrów muszą być takie same jak w przypadku klasy bazowej. Typ danych wyjściowych przeciążenia może być inny.
W przeciwieństwie do konstruktorów metody nie mogą używać składni : base(<parameters>) do wywoływania przeciążenia klasy bazowej dla metody . Ponownie zdefiniowane przeciążenie klasy pochodnej całkowicie zastępuje przeciążenie zdefiniowane przez klasę bazową. Aby wywołać metodę klasy bazowej dla wystąpienia, przed wywołaniem metody należy rzutować zmienną wystąpienia ($this) na klasę bazową.
Poniższy fragment kodu pokazuje, jak klasa pochodna może wywołać metodę klasy bazowej.
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
Aby zapoznać się z rozszerzonym przykładem pokazującym, jak klasa pochodna może zastąpić dziedziczone metody, zobacz klasę Ilustracja w Przykład 1.
Właściwości klasy pochodnej
Gdy klasa pochodzi z klasy bazowej, dziedziczy właściwości klasy bazowej. Wszystkie właściwości zdefiniowane w klasie bazowej, w tym ukryte właściwości, są dostępne w klasie pochodnej.
Klasa pochodna może zastąpić dziedziczona właściwość, ponownie definiując ją w definicji klasy. Właściwość klasy pochodnej używa ponownie zdefiniowanego typu i wartości domyślnej, jeśli istnieje. Jeśli dziedziczona właściwość zdefiniowała wartość domyślną, a właściwość ponownie zdefiniowana nie, dziedziczona właściwość nie ma wartości domyślnej.
Jeśli klasa pochodna nie zastępuje właściwości statycznej, uzyskiwanie dostępu do właściwości statycznej za pośrednictwem klasy pochodnej uzyskuje dostęp do właściwości statycznej klasy bazowej. Modyfikowanie wartości właściwości za pomocą klasy pochodnej modyfikuje wartość w klasie bazowej. Każda inna klasa pochodna, która nie zastępuje właściwości statycznej, używa również wartości właściwości w klasie bazowej. Aktualizowanie wartości odziedziczonej właściwości statycznej w klasie, która nie zastępuje właściwości, może mieć niezamierzone efekty dla klas pochodnych z tej samej klasy bazowej.
Przykład 1 pokazuje, jak klasy pochodne dziedziczą, rozszerzają i zastępują właściwości klasy bazowej.
Wyprowadzanie z typów ogólnych
Gdy klasa pochodzi z klasy ogólnej, parametr typu musi być już zdefiniowany przed przeanalizowaniem klasy pochodnej przez program PowerShell. Jeśli parametr typu dla klasy ogólnej jest klasą programu PowerShell lub wyliczeniem zdefiniowanym w tym samym pliku lub bloku kodu, program PowerShell zgłasza błąd.
Aby uzyskać klasę z ogólnej klasy bazowej z typem niestandardowym jako parametr typu, zdefiniuj klasę lub wyliczenie dla parametru typu w innym pliku lub module i użyj instrukcji using module, aby załadować definicję typu.
Przykład pokazujący, jak dziedziczyć z ogólnej klasy bazowej, zobacz Przykład 3.
Przydatne klasy do dziedziczenia
Istnieje kilka klas, które mogą być przydatne do dziedziczenia podczas tworzenia modułów programu PowerShell. W tej sekcji wymieniono kilka klas bazowych i elementy, dla których można użyć klasy pochodnej.
- System.Attribute — klasy pochodne definiujące atrybuty, które mogą być używane dla zmiennych, parametrów, klas i wyliczenia itd.
- System.Management.Automation.ArgumentTransformationAttribute — klasy pochodne do obsługi konwersji danych wejściowych dla zmiennej lub parametru na określony typ danych.
- System.Management.Automation.ValidateArgumentsAttribute — klasy pochodne, aby zastosować niestandardową walidację do zmiennych, parametrów i właściwości klasy.
- System.Collections.Generic.List — klasy pochodne ułatwiające tworzenie list określonego typu danych i zarządzanie nimi.
- System.Exception — klasy pochodne definiujące błędy niestandardowe.
Implementowanie interfejsów
Klasa programu PowerShell, która implementuje interfejs, musi implementować wszystkie elementy członkowskie tego interfejsu. Pominięcie elementów członkowskich interfejsu implementacji powoduje błąd analizy w czasie wykonywania skryptu.
Nuta
Program PowerShell nie obsługuje deklarowania nowych interfejsów w skryscie programu PowerShell.
Zamiast tego interfejsy muszą być zadeklarowane w kodzie platformy .NET i dodane do sesji za pomocą polecenia cmdlet Add-Type lub instrukcji using assembly.
Gdy klasa implementuje interfejs, może być używana jak każda inna klasa, która implementuje ten interfejs. Niektóre polecenia i operacje ograniczają obsługiwane typy do klas implementujących określony interfejs.
Aby zapoznać się z przykładową implementacją interfejsów, zobacz Przykład 2.
Przydatne interfejsy do zaimplementowania
Istnieje kilka klas interfejsów, które mogą być przydatne do dziedziczenia podczas tworzenia modułów programu PowerShell. W tej sekcji wymieniono kilka klas bazowych i elementy, dla których można użyć klasy pochodnej.
- System.IEquatable — ten interfejs umożliwia użytkownikom porównywanie dwóch wystąpień klasy. Jeśli klasa nie implementuje tego interfejsu, program PowerShell sprawdza równoważność między dwoma wystąpieniami przy użyciu równości odwołań. Innymi słowy, wystąpienie klasy równa się tylko samemu, nawet jeśli wartości właściwości w dwóch wystąpieniach są takie same.
-
System.IComparable — ten interfejs umożliwia użytkownikom porównywanie wystąpień klasy z operatorami porównania
-le,-lt,-gei-gt. Jeśli klasa nie implementuje tego interfejsu, te operatory zgłaszają błąd. - System.IFormattable — ten interfejs umożliwia użytkownikom formatowanie wystąpień klasy w różnych ciągach. Jest to przydatne w przypadku klas, które mają więcej niż jedną standardową reprezentację ciągu, taką jak elementy budżetu, bibliografie i temperatury.
- System.IConvertible — ten interfejs umożliwia użytkownikom konwertowanie wystąpień klasy na inne typy środowiska uruchomieniowego. Jest to przydatne w przypadku klas, które mają podstawową wartość liczbową lub można je przekonwertować na jedną.
Ograniczenia
Program PowerShell nie obsługuje definiowania interfejsów w kodzie skryptu.
Obejście: Zdefiniuj interfejsy w języku C# i odwołuj się do zestawu, który definiuje interfejsy.
Klasy programu PowerShell mogą dziedziczyć tylko z jednej klasy bazowej.
Obejście: Dziedziczenie klas jest przechodnie. Klasa pochodna może dziedziczyć z innej klasy pochodnej, aby uzyskać właściwości i metody klasy bazowej.
Podczas dziedziczenia z klasy ogólnej lub interfejsu należy już zdefiniować parametr typu dla klasy ogólnej. Klasa nie może zdefiniować się jako parametr typu dla klasy lub interfejsu.
Obejście: Aby pochodzić z ogólnej klasy bazowej lub interfejsu, zdefiniuj typ niestandardowy w innym pliku
.psm1i użyj instrukcjiusing module, aby załadować typ. Nie ma obejścia dla typu niestandardowego, aby używać go jako parametru typu podczas dziedziczenia po typie ogólnym.