Sdílet prostřednictvím


O třídách

Stručný popis

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

Dlouhý popis

PowerShell 5.0 přidává 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 IT specialistům osvojit si PowerShell pro širší škálu případů použití. Zjednodušuje vývoj artefaktů PowerShellu a urychluje pokrytí ploch pro správu.

Deklarace třídy je podrobný plán sloužící k vytvoř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 Device, $dev jedná se o objekt nebo instanci typu Device. Každá instance zařízení může mít ve svých vlastnostech různé hodnoty.

Podporované scénáře

  • Definování vlastních typů v PowerShellu pomocí známé sémantiky objektově orientovaného programování, jako jsou třídy, vlastnosti, metody, dědičnost atd.
  • Typy ladění pomocí jazyka PowerShell.
  • 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> ...]
}

Instance tříd 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 kolem názvu třídy povinné hranaté závorky. Hranaté 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ého typu nebo instance jiné třídy. Třídy nemají žádné omezení v 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říklad komplexních typů ve vlastnostech třídy

Tento příklad definuje prázdnou třídu Rack pomocí třídy Device . Následující příklady ukazují, jak přidat zařízení do racku a jak začít s předem nabitý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 můžou vracet výstup. Data vrácená metodou můžou být libovolného definovaného datového typu.

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

Rozšíření třídy Rack o přidávání a odebírá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řídy

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 s výjimkou objektů uvedených v return příkazu . Z kódu do kanálu nedochází k náhodnému výstupu.

Poznámka

To se v zásadě liší od způsobu, jakým funkce PowerShellu zpracovávají výstup, kde všechno směřuje do kanálu.

Výstup metody

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

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 mohou mít argumenty pro inicializaci datových členů nového objektu.

Třída může mít definovaný žádný nebo více konstruktorů. Pokud není definován žádný konstruktor, je třídě přidělen výchozí konstruktor bez parametrů. Tento konstruktor inicializuje všechny členy na jejich výchozí hodnoty. Typům objektů a řetězcům se přidělují hodnoty null. Při definování konstruktoru se nevytvořil žádný výchozí konstruktor bez parametrů. Create konstruktor bez parametrů, pokud je potřeba.

Základní syntaxe konstruktoru

V tomto příkladu je třída Device definovaná pomocí vlastností a konstruktoru. Chcete-li použít tuto třídu, je uživatel povinen zadat hodnoty 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 definovaná s vlastnostmi, výchozím konstruktorem a konstruktorem pro inicializaci instance.

Výchozí konstruktor nastaví značku na Undefined a ponechá model a vendor-sku 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 znemožňuje viditelnost vlastnosti nebo metody. Vlastnost nebo metoda je uživateli stále přístupná a je k dispozici ve všech oborech, ve kterých je objekt k dispozici. Skryté členy jsou skryté z Get-Member rutiny a nelze je zobrazit pomocí dokončování tabulátoru nebo Technologie 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, kterou byste neměli v žádném okamžiku měnit. Tato hodnota je známa při vytváření.

Použití atributu hidden umožňuje vývojáři ponechat počet slotů skrytý 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 Slots (Sloty ) se ve $r1 výstupu nezobrazuje. Velikost však byla změněna konstruktorem.

Statický atribut

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

Statická vlastnost je vždy k dispozici, nezávisle na vytvoření instance třídy. Statická vlastnost je sdílena napříč všemi instancemi 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 datacentru existují zde uvedené instance racků. Proto byste chtěli mít přehled o rackech v 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 existuje

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 při každém spuštění tohoto příkladu se počet stojanů zvyšuje.

Atributy ověření vlastnosti

Ověřovací atributy umožňují testovat, že hodnoty zadané vlastnostem 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 ve třídách PowerShellu

Třídu můžete rozšířit vytvořením nové třídy, která je odvozena z 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ícenásobnou dědičnost. Třídy nemohou dědit z více než jedné třídy. Pro tento účel však můžete použít rozhraní.

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

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 s deklarací rozhraní přicházející po základní třídě.

Class Derived : Base.Interface {...}

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

V tomto příkladu jsou třídy Rack a Zařízení použité v předchozích příkladech lépe definovány tak, aby se zabránilo opakování vlastností, lépe sladily společné vlastnosti a znovu používaly společnou obchodní logiku.

Většina objektů v datacentru jsou prostředky společnosti, což dává smysl začít je sledovat jako prostředky. Typy zařízení jsou definovány výčtem DeviceType , podrobnosti o výčtech najdete v about_Enum .

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

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

Chcete-li vyvolat konstruktor základní třídy z podtřídy, přidejte base klíčové 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řepsané implementace, 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 za základními typy nebo bezprostředně za dvojtečku (:), pokud není zadaný žá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 a příkaz #requires importuje pouze funkce modulu, aliasy a proměnné definované 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é