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 ; ComputeServer
obě.
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.