O třídách

Krátký popis

Popisuje, jak můžete pomocí tříd vytvářet vlastní typy.

Dlouhý popis

PowerShell 5.0 přidá formální syntaxi pro definování tříd a dalších uživatelsky definovaných typů. Přidání tříd umožňuje vývojářům a odborníkům v oblasti IT využívat PowerShell pro širší škálu případů použití. Zjednodušuje vývoj artefaktů PowerShellu a urychluje pokrytí povrchů správy.

Deklarace třídy je podrobný plán, který se používá k vytváření instancí objektů za běhu. Když definujete třídu, název třídy je název typu. Pokud například deklarujete třídu s názvem Device a inicializujete proměnnou $dev na novou instanci zařízení, $dev je objekt nebo instance typu Device. Každá instance zařízení může mít různé hodnoty ve vlastnostech.

Podporované scénáře

  • Definujte vlastní typy v PowerShellu pomocí známé sémantiky objektově orientovaného programování, jako jsou třídy, vlastnosti, metody, dědičnost atd.
  • Ladění typů pomocí jazyka PowerShellu
  • Generování a zpracování výjimek pomocí formálních mechanismů
  • Definujte prostředky DSC a jejich přidružené typy pomocí jazyka PowerShellu.

Syntax

Třídy jsou deklarovány pomocí následující syntaxe:

class <class-name> [: [<base-class>][,<interface-list]] {
    [[<attribute>] [hidden] [static] <property-definition> ...]
    [<class-name>([<constructor-argument-list>])
      {<constructor-statement-list>} ...]
    [[<attribute>] [hidden] [static] <method-definition> ...]
}

Třídy se vytvářejí pomocí některé z následujících syntaxí:

[$<variable-name> =] New-Object -TypeName <class-name> [
  [-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])

Poznámka

Při použití [<class-name>]::new( syntaxe jsou závorky kolem názvu třídy povinné. Závorky signalizují definici typu pro PowerShell.

Příklad syntaxe a použití

Tento příklad ukazuje minimální syntaxi potřebnou k vytvoření použitelné třídy.

class Device {
    [string]$Brand
}

$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft

Vlastnosti třídy

Vlastnosti jsou proměnné deklarované v oboru třídy. Vlastnost může být libovolného předdefinovaný typ nebo instance jiné třídy. Třídy nemají žádné omezení počtu vlastností, které mají.

Příklad třídy s jednoduchými vlastnostmi

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

$device = [Device]::new()
$device.Brand = "Microsoft"
$device.Model = "Surface Pro 4"
$device.VendorSku = "5072641000"

$device
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Příklady složitých typů ve vlastnostech třídy

Tento příklad definuje prázdnou třídu racku pomocí třídy Device . Následující příklady ukazují, jak přidat zařízení do racku a jak začít s předpřipraveným rackem.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
}

class Rack {
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new(8)

}

$rack = [Rack]::new()

$rack

Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, $null, $null...}


Metody tříd

Metody definují akce, které mohou třídy provádět. Metody mohou přijímat parametry, které poskytují vstupní data. Metody mohou vracet výstup. Data vrácená metodou mohou být libovolným definovaným datovým typem.

Příklad jednoduché třídy s vlastnostmi a metodami

Rozšíření třídy Rack pro přidání a odebrání zařízení do nebo z ní.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    [string]ToString(){
        return ("{0}|{1}|{2}" -f $this.Brand, $this.Model, $this.VendorSku)
    }
}

class Rack {
    [int]$Slots = 8
    [string]$Brand
    [string]$Model
    [string]$VendorSku
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void]RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }

    [int[]] GetAvailableSlots(){
        [int]$i = 0
        return @($this.Devices.foreach{ if($_ -eq $null){$i}; $i++})
    }
}

$rack = [Rack]::new()

$surface = [Device]::new()
$surface.Brand = "Microsoft"
$surface.Model = "Surface Pro 4"
$surface.VendorSku = "5072641000"

$rack.AddDevice($surface, 2)

$rack
$rack.GetAvailableSlots()

Slots     : 8
Brand     :
Model     :
VendorSku :
AssetId   :
Devices   : {$null, $null, Microsoft|Surface Pro 4|5072641000, $null...}

0
1
3
4
5
6
7

Výstup v metodách tříd

Metody by měly mít definovaný návratový typ. Pokud metoda nevrací výstup, měl by být [void]typ výstupu .

V metodách třídy se do kanálu neposílají žádné objekty kromě objektů uvedených v return příkazu. Z kódu neexistuje žádný náhodný výstup kanálu.

Poznámka

To se v podstatě liší od toho, jak funkce PowerShellu zpracovávají výstup, kde všechno směřuje do kanálu.

Výstup metody

Tento příklad ukazuje žádný náhodný výstup kanálu z metod třídy, s výjimkou příkazu return .

class FunWithIntegers
{
    [int[]]$Integers = 0..10

    [int[]]GetOddIntegers(){
        return $this.Integers.Where({ ($_ % 2) })
    }

    [void] GetEvenIntegers(){
        # this following line doesn't go to the pipeline
        $this.Integers.Where({ ($_ % 2) -eq 0})
    }

    [string]SayHello(){
        # this following line doesn't go to the pipeline
        "Good Morning"

        # this line goes to the pipeline
        return "Hello World"
    }
}

$ints = [FunWithIntegers]::new()

$ints.GetOddIntegers()

$ints.GetEvenIntegers()

$ints.SayHello()
1
3
5
7
9
Hello World

Konstruktor

Konstruktory umožňují nastavit výchozí hodnoty a ověřit logiku objektu v okamžiku vytvoření instance třídy. Konstruktory mají stejný název jako třída. Konstruktory můžou mít argumenty pro inicializaci datových členů nového objektu.

Třída může mít definované nulové nebo více konstruktorů. Pokud není definován žádný konstruktor, třída má výchozí konstruktor bez parametrů. Tento konstruktor inicializuje všechny členy na výchozí hodnoty. Typy objektů a řetězce mají hodnoty null. Při definování konstruktoru se nevytvořil žádný výchozí konstruktor bez parametrů. Pokud je potřeba, vytvořte konstruktor bez parametrů.

Základní syntaxe konstruktoru

V tomto příkladu je třída Device definována s vlastnostmi a konstruktorem. Chcete-li použít tuto třídu, je uživatel vyžadován k zadání hodnot parametrů uvedených v konstruktoru.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$surface
Brand     Model         VendorSku
-----     -----         ---------
Microsoft Surface Pro 4 5072641000

Příklad s více konstruktory

V tomto příkladu je třída Device definována s vlastnostmi, výchozím konstruktorem a konstruktorem pro inicializaci instance.

Výchozí konstruktor nastaví značku na Hodnotu Undefined a ponechá model a skladovou položku dodavatele s hodnotami null.

class Device {
    [string]$Brand
    [string]$Model
    [string]$VendorSku

    Device(){
        $this.Brand = 'Undefined'
    }

    Device(
        [string]$b,
        [string]$m,
        [string]$vsk
    ){
        $this.Brand = $b
        $this.Model = $m
        $this.VendorSku = $vsk
    }
}

[Device]$somedevice = [Device]::new()
[Device]$surface = [Device]::new("Microsoft", "Surface Pro 4", "5072641000")

$somedevice
$surface
Brand       Model           VendorSku
-----       -----           ---------
Undefined
Microsoft   Surface Pro 4   5072641000

Skrytý atribut

Atribut hidden zviditelní vlastnost nebo metodu. Vlastnost nebo metoda je stále přístupná uživateli a je k dispozici ve všech oborech, ve kterých je objekt k dispozici. Skryté členy jsou z Get-Member rutiny skryté a nelze je zobrazit pomocí dokončování tabulátoru nebo IntelliSense mimo definici třídy.

Příklad použití skrytých atributů

Při vytvoření objektu Rack je počet slotů pro zařízení pevnou hodnotou, která by se neměla kdykoli změnit. Tato hodnota je známa při vytváření.

Použití skrytého atributu umožňuje vývojáři zachovat počet slotů skrytých a zabránit neúmyslným změnám velikosti racku.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    [int] hidden $Slots = 8
    [string]$Brand
    [string]$Model
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)
    }
}

[Rack]$r1 = [Rack]::new("Microsoft", "Surface Pro 4", 16)

$r1
$r1.Devices.Length
$r1.Slots
Brand     Model         Devices
-----     -----         -------
Microsoft Surface Pro 4 {$null, $null, $null, $null...}
16
16

Všimněte si , že vlastnost Sloty se ve výstupu nezobrazuje $r1 . Velikost však změnil konstruktor.

Statický atribut

Atribut static definuje vlastnost nebo metodu, která existuje ve třídě a nepotřebuje žádnou instanci.

Statická vlastnost je vždy dostupná, nezávisle na instanci třídy. Statická vlastnost se sdílí ve všech instancích třídy. Statická metoda je k dispozici vždy. Všechny statické vlastnosti jsou aktivní pro celý rozsah relace.

Příklad použití statických atributů a metod

Předpokládejme, že v datovém centru existují instance racků. Proto byste chtěli sledovat racky ve vašem kódu.

class Device {
    [string]$Brand
    [string]$Model
}

class Rack {
    hidden [int] $Slots = 8
    static [Rack[]]$InstalledRacks = @()
    [string]$Brand
    [string]$Model
    [string]$AssetId
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack ([string]$b, [string]$m, [string]$id, [int]$capacity){
        ## argument validation here

        $this.Brand = $b
        $this.Model = $m
        $this.AssetId = $id
        $this.Slots = $capacity

        ## reset rack size to new capacity
        $this.Devices = [Device[]]::new($this.Slots)

        ## add rack to installed racks
        [Rack]::InstalledRacks += $this
    }

    static [void]PowerOffRacks(){
        foreach ($rack in [Rack]::InstalledRacks) {
            Write-Warning ("Turning off rack: " + ($rack.AssetId))
        }
    }
}

Testování statické vlastnosti a metody existují

PS> [Rack]::InstalledRacks.Length
0

PS> [Rack]::PowerOffRacks()

PS> (1..10) | ForEach-Object {
>>   [Rack]::new("Adatum Corporation", "Standard-16",
>>     $_.ToString("Std0000"), 16)
>> } > $null

PS> [Rack]::InstalledRacks.Length
10

PS> [Rack]::InstalledRacks[3]
Brand              Model       AssetId Devices
-----              -----       ------- -------
Adatum Corporation Standard-16 Std0004 {$null, $null, $null, $null...}

PS> [Rack]::PowerOffRacks()
WARNING: Turning off rack: Std0001
WARNING: Turning off rack: Std0002
WARNING: Turning off rack: Std0003
WARNING: Turning off rack: Std0004
WARNING: Turning off rack: Std0005
WARNING: Turning off rack: Std0006
WARNING: Turning off rack: Std0007
WARNING: Turning off rack: Std0008
WARNING: Turning off rack: Std0009
WARNING: Turning off rack: Std0010

Všimněte si, že počet racků se při každém spuštění tohoto příkladu zvyšuje.

Atributy ověření vlastnosti

Ověřovací atributy umožňují testovat, že hodnoty zadané vlastnostmi splňují definované požadavky. Ověření se aktivuje v okamžiku, kdy je hodnota přiřazena. Viz about_functions_advanced_parameters.

Příklad použití ověřovacích atributů

class Device {
    [ValidateNotNullOrEmpty()][string]$Brand
    [ValidateNotNullOrEmpty()][string]$Model
}

[Device]$dev = [Device]::new()

Write-Output "Testing dev"
$dev

$dev.Brand = ""
Testing dev

Brand Model
----- -----

Exception setting "Brand": "The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again."
At C:\tmp\Untitled-5.ps1:11 char:1
+ $dev.Brand = ""
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

Dědičnost v třídách PowerShellu

Třídu můžete rozšířit vytvořením nové třídy, která je odvozena od existující třídy. Odvozená třída dědí vlastnosti základní třídy. Podle potřeby můžete přidat nebo přepsat metody a vlastnosti.

PowerShell nepodporuje více dědičnosti. Třídy nemohou dědit z více než jedné třídy. Pro tento účel však můžete používat rozhraní.

Implementace dědičnosti je definována operátorem : ; to znamená rozšířit tuto třídu nebo implementovat tato rozhraní. Odvozená třída by měla být vždy vlevo v deklaraci třídy.

Příklad použití jednoduché syntaxe dědičnosti

Tento příklad ukazuje jednoduchou syntaxi dědičnosti tříd PowerShellu.

Class Derived : Base {...}

Tento příklad ukazuje dědičnost pomocí deklarace rozhraní přicházející po základní třídě.

Class Derived : Base.Interface {...}

Příklad jednoduché dědičnosti v třídách PowerShellu

V tomto příkladu jsou třídy racku a zařízení použité v předchozích příkladech lépe definovány tak, aby se zabránilo opakování vlastností, lepší sladění běžných vlastností a opakované použití běžné obchodní logiky.

Většina objektů v datovém centru je firemní aktiva, která dává smysl začít je sledovat jako aktiva. Typy zařízení jsou definovány výčtem DeviceType , viz about_Enum podrobnosti o výčtech.

V našem příkladu definujeme Rack pouze rozšíření třídy a ComputeServerobě rozšíření třídy Device .

enum DeviceType {
    Undefined = 0
    Compute = 1
    Storage = 2
    Networking = 4
    Communications = 8
    Power = 16
    Rack = 32
}

class Asset {
    [string]$Brand
    [string]$Model
}

class Device : Asset {
    hidden [DeviceType]$devtype = [DeviceType]::Undefined
    [string]$Status

    [DeviceType] GetDeviceType(){
        return $this.devtype
    }
}

class ComputeServer : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Compute
    [string]$ProcessorIdentifier
    [string]$Hostname
}

class Rack : Device {
    hidden [DeviceType]$devtype = [DeviceType]::Rack
    hidden [int]$Slots = 8

    [string]$Datacenter
    [string]$Location
    [Device[]]$Devices = [Device[]]::new($this.Slots)

    Rack (){
        ## Just create the default rack with 8 slots
    }

    Rack ([int]$s){
        ## Add argument validation logic here
        $this.Devices = [Device[]]::new($s)
    }

    [void] AddDevice([Device]$dev, [int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $dev
    }

    [void] RemoveDevice([int]$slot){
        ## Add argument validation logic here
        $this.Devices[$slot] = $null
    }
}

$FirstRack = [Rack]::new(16)
$FirstRack.Status = "Operational"
$FirstRack.Datacenter = "PNW"
$FirstRack.Location = "F03R02.J10"

(0..15).ForEach({
    $ComputeServer = [ComputeServer]::new()
    $ComputeServer.Brand = "Fabrikam, Inc."       ## Inherited from Asset
    $ComputeServer.Model = "Fbk5040"              ## Inherited from Asset
    $ComputeServer.Status = "Installed"           ## Inherited from Device
    $ComputeServer.ProcessorIdentifier = "x64"    ## ComputeServer
    $ComputeServer.Hostname = ("r1s" + $_.ToString("000")) ## ComputeServer
    $FirstRack.AddDevice($ComputeServer, $_)
  })

$FirstRack
$FirstRack.Devices
Datacenter : PNW
Location   : F03R02.J10
Devices    : {r1s000, r1s001, r1s002, r1s003...}
Status     : Operational
Brand      :
Model      :

ProcessorIdentifier : x64
Hostname            : r1s000
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

ProcessorIdentifier : x64
Hostname            : r1s001
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

<... content truncated here for brevity ...>

ProcessorIdentifier : x64
Hostname            : r1s015
Status              : Installed
Brand               : Fabrikam, Inc.
Model               : Fbk5040

Volání konstruktorů základní třídy

Pokud chcete vyvolat konstruktor základní třídy z podtřídy, přidejte klíčové base slovo.

class Person {
    [int]$Age

    Person([int]$a)
    {
        $this.Age = $a
    }
}

class Child : Person
{
    [string]$School

    Child([int]$a, [string]$s ) : base($a) {
        $this.School = $s
    }
}

[Child]$littleone = [Child]::new(10, "Silver Fir Elementary School")

$littleone.Age

10

Vyvolání metod základní třídy

Chcete-li přepsat existující metody v podtřídách, deklarujte metody pomocí stejného názvu a podpisu.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
}

[ChildClass1]::new().days()

2

Chcete-li volat metody základní třídy z přepsáných implementací, přetypujte na základní třídu ([baseclass]$this) při vyvolání.

class BaseClass
{
    [int]days() {return 1}
}
class ChildClass1 : BaseClass
{
    [int]days () {return 2}
    [int]basedays() {return ([BaseClass]$this).days()}
}

[ChildClass1]::new().days()
[ChildClass1]::new().basedays()

2
1

Rozhraní

Syntaxe pro deklarování rozhraní je podobná jazyku C#. Rozhraní můžete deklarovat po základních typech nebo bezprostředně za dvojtečku (:), pokud není zadán žádný základní typ. Všechny názvy typů oddělte čárkami.

class MyComparable : system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

class MyComparableBar : bar, system.IComparable
{
    [int] CompareTo([object] $obj)
    {
        return 0;
    }
}

Import tříd z modulu PowerShellu

Import-Module#requires a příkaz importuje pouze funkce modulu, aliasy a proměnné, jak je definováno modulem. Třídy se neimportují. Příkaz using module importuje třídy definované v modulu. Pokud se modul nenačte v aktuální relaci, using příkaz selže.

Viz také