Over klassen
Korte beschrijving
Hierin wordt beschreven hoe u klassen kunt gebruiken om uw eigen aangepaste typen te maken.
Lange beschrijving
PowerShell 5.0 voegt een formele syntaxis toe om klassen en andere door de gebruiker gedefinieerde typen te definiëren. Door de toevoeging van klassen kunnen ontwikkelaars en IT-professionals PowerShell gebruiken voor een breder scala aan gebruiksvoorbeelden. Het vereenvoudigt de ontwikkeling van PowerShell-artefacten en versnelt de dekking van beheeroppervlakken.
Een klassedeclaratie is een blauwdruk die wordt gebruikt voor het maken van exemplaren van objecten tijdens runtime. Wanneer u een klasse definieert, is de klassenaam de naam van het type. Als u bijvoorbeeld een klasse met de naam Apparaat declareert en een variabele $dev
initialiseert naar een nieuw exemplaar van Apparaat, $dev
is dit een object of exemplaar van het type Apparaat. Elk apparaatexemplaar kan verschillende waarden in de eigenschappen hebben.
Ondersteunde scenario's
- Definieer aangepaste typen in PowerShell met behulp van vertrouwde objectgeoriënteerde programmeersemantiek, zoals klassen, eigenschappen, methoden, overname, enzovoort.
- Foutopsporingstypen met behulp van de PowerShell-taal.
- Uitzonderingen genereren en afhandelen met behulp van formele mechanismen.
- Definieer DSC-resources en de bijbehorende typen met behulp van de PowerShell-taal.
Syntax
Klassen worden gedeclareerd met behulp van de volgende syntaxis:
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> ...]
}
Klassen worden geïnstantieerd met behulp van een van de volgende syntaxis:
[$<variable-name> =] New-Object -TypeName <class-name> [
[-ArgumentList] <constructor-argument-list>]
[$<variable-name> =] [<class-name>]::new([<constructor-argument-list>])
Notitie
Wanneer u de [<class-name>]::new(
syntaxis gebruikt, zijn vierkante haken rond de klassenaam verplicht. De haakjes geven een typedefinitie voor PowerShell aan.
Voorbeeldsyntaxis en gebruik
In dit voorbeeld ziet u de minimale syntaxis die nodig is om een bruikbare klasse te maken.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft
Klasse-eigenschappen
Eigenschappen zijn variabelen die zijn gedeclareerd in het klassebereik. Een eigenschap kan van een ingebouwd type zijn of een exemplaar van een andere klasse. Klassen hebben geen beperking in het aantal eigenschappen dat ze hebben.
Voorbeeldklasse met eenvoudige eigenschappen
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
Voorbeeld van complexe typen in klasse-eigenschappen
In dit voorbeeld wordt een lege Rack-klasse gedefinieerd met behulp van de klasse Apparaat . In de volgende voorbeelden ziet u hoe u apparaten toevoegt aan het rek en hoe u kunt beginnen met een vooraf geladen rek.
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...}
Klassemethoden
Methoden definiëren de acties die een klasse kan uitvoeren. Methoden kunnen parameters gebruiken die invoergegevens leveren. Methoden kunnen uitvoer retourneren. Gegevens die door een methode worden geretourneerd, kunnen elk gedefinieerd gegevenstype zijn.
Voorbeeld van een eenvoudige klasse met eigenschappen en methoden
Uitbreiding van de Rack-klasse om apparaten toe te voegen of eruit te verwijderen.
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
Uitvoer in klassemethoden
Voor methoden moet een retourtype zijn gedefinieerd. Als een methode geen uitvoer retourneert, moet het uitvoertype zijn [void]
.
In klassemethoden worden er geen objecten naar de pijplijn verzonden, behalve de objecten die worden vermeld in de return
-instructie. Er is geen onbedoelde uitvoer van de code naar de pijplijn.
Notitie
Dit is fundamenteel anders dan hoe PowerShell-functies uitvoer verwerken, waarbij alles naar de pijplijn gaat.
Methode-uitvoer
In dit voorbeeld wordt geen onbedoelde uitvoer naar de pijplijn van klassemethoden gedemonstreert, met uitzondering van de return
-instructie.
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
Constructor
Met constructors kunt u standaardwaarden instellen en objectlogica valideren op het moment dat het exemplaar van de klasse wordt gemaakt. Constructors hebben dezelfde naam als de klasse. Constructors kunnen argumenten hebben om de gegevensleden van het nieuwe object te initialiseren.
Voor de klasse kunnen nul of meer constructors zijn gedefinieerd. Als er geen constructor is gedefinieerd, krijgt de klasse een standaardconstructor zonder parameter. Deze constructor initialiseert alle leden naar hun standaardwaarden. Objecttypen en tekenreeksen krijgen null-waarden. Wanneer u de constructor definieert, wordt er geen standaardconstructor zonder parameter gemaakt. Creatie indien nodig een constructor zonder parameters.
Basissyntaxis van constructor
In dit voorbeeld wordt de klasse Apparaat gedefinieerd met eigenschappen en een constructor. Als u deze klasse wilt gebruiken, moet de gebruiker waarden opgeven voor de parameters die worden vermeld in de constructor.
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
Voorbeeld met meerdere constructors
In dit voorbeeld wordt de klasse Apparaat gedefinieerd met eigenschappen, een standaardconstructor en een constructor om het exemplaar te initialiseren.
De standaardconstructor stelt het merk in op Undefined en laat het model en de vendor-sku null-waarden over.
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
Verborgen kenmerk
Het hidden
kenmerk maakt een eigenschap of methode minder zichtbaar. De eigenschap of methode is nog steeds toegankelijk voor de gebruiker en is beschikbaar in alle bereiken waarin het object beschikbaar is. Verborgen leden zijn verborgen in de Get-Member
cmdlet en kunnen niet worden weergegeven met behulp van tabvoltooiing of IntelliSense buiten de klassedefinitie.
Voorbeeld met verborgen kenmerken
Wanneer een Rack-object wordt gemaakt, is het aantal sleuven voor apparaten een vaste waarde die op geen enkel moment mag worden gewijzigd. Deze waarde is bekend tijdens het maken.
Met behulp van het verborgen kenmerk kan de ontwikkelaar het aantal sleuven verborgen houden en voorkomt onbedoelde wijzigingen van de grootte van het rack.
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
De eigenschap Slots wordt niet weergegeven in $r1
de uitvoer. De grootte is echter gewijzigd door de constructor.
Statisch kenmerk
Het static
kenmerk definieert een eigenschap of een methode die bestaat in de klasse en geen exemplaar nodig heeft.
Er is altijd een statische eigenschap beschikbaar, onafhankelijk van klasse-instantiëring. Een statische eigenschap wordt gedeeld door alle exemplaren van de klasse. Er is altijd een statische methode beschikbaar. Alle statische eigenschappen zijn live gedurende de hele sessieperiode.
Voorbeeld met statische kenmerken en methoden
Stel dat de rekken die hier zijn geïnstantieerd, aanwezig zijn in uw datacenter. U wilt dus de rekken in uw code bijhouden.
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))
}
}
}
Statische eigenschap en methode testen
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
U ziet dat het aantal rekken toeneemt wanneer u dit voorbeeld uitvoert.
Eigenschapsvalidatiekenmerken
Met validatiekenmerken kunt u testen of waarden die aan eigenschappen worden gegeven, voldoen aan gedefinieerde vereisten. Validatie wordt geactiveerd op het moment dat de waarde wordt toegewezen. Zie about_functions_advanced_parameters.
Voorbeeld met validatiekenmerken
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
Overname in PowerShell-klassen
U kunt een klasse uitbreiden door een nieuwe klasse te maken die is afgeleid van een bestaande klasse. De afgeleide klasse neemt de eigenschappen van de basisklasse over. U kunt naar behoefte methoden en eigenschappen toevoegen of overschrijven.
PowerShell biedt geen ondersteuning voor meervoudige overname. Klassen kunnen niet worden overgenomen van meer dan één klasse. Hiervoor kunt u echter wel interfaces gebruiken.
Overname-implementatie wordt gedefinieerd door de :
operator. Dit betekent dat deze klasse moet worden uitgebreid of deze interfaces worden geïmplementeerd. De afgeleide klasse moet altijd het meest links zijn in de klassedeclaratie.
Voorbeeld met eenvoudige syntaxis voor overname
In dit voorbeeld ziet u de eenvoudige syntaxis voor overname van PowerShell-klassen.
Class Derived : Base {...}
In dit voorbeeld ziet u overname met een interfacedeclaratie na de basisklasse.
Class Derived : Base.Interface {...}
Voorbeeld van eenvoudige overname in PowerShell-klassen
In dit voorbeeld zijn de klassen Rack en Device die in de vorige voorbeelden zijn gebruikt, beter gedefinieerd om herhalingen van eigenschappen te voorkomen, algemene eigenschappen beter uit te lijnen en algemene bedrijfslogica opnieuw te gebruiken.
De meeste objecten in het datacenter zijn bedrijfsassets, wat zinvol is om ze als assets bij te houden. Apparaattypen worden gedefinieerd door de DeviceType
opsomming. Zie about_Enum voor meer informatie over opsommingen.
In ons voorbeeld definiëren Rack
we alleen en ; ComputeServer
beide extensies voor de Device
klasse.
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
Basisklasseconstructors aanroepen
Als u een basisklasseconstructor wilt aanroepen vanuit een subklasse, voegt u het base
trefwoord toe.
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
Basisklassemethoden aanroepen
Als u bestaande methoden in subklassen wilt overschrijven, declareert u methoden met dezelfde naam en handtekening.
class BaseClass
{
[int]days() {return 1}
}
class ChildClass1 : BaseClass
{
[int]days () {return 2}
}
[ChildClass1]::new().days()
2
Als u basisklassemethoden wilt aanroepen vanuit overschreven implementaties, cast u naar de basisklasse ([basisklasse]$this) bij aanroep.
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
Interfaces
De syntaxis voor het declareren van interfaces is vergelijkbaar met C#. U kunt interfaces declareren na basistypen of direct na een dubbele punt (:
) wanneer er geen basistype is opgegeven. Scheid alle typenamen met komma's.
class MyComparable : system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
class MyComparableBar : bar, system.IComparable
{
[int] CompareTo([object] $obj)
{
return 0;
}
}
Klassen importeren uit een PowerShell-module
Import-Module
en de #requires
instructie importeert alleen de modulefuncties, aliassen en variabelen, zoals gedefinieerd door de module. Klassen worden niet geïmporteerd. Met using module
de instructie worden de klassen geïmporteerd die in de module zijn gedefinieerd. Als de module niet wordt geladen in de huidige sessie, mislukt de using
instructie.