about_Classes
Korte beschrijving
Beschrijft 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 gebruiksgevallen. 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 run time. 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 initialiseert naar een nieuw exemplaar van $dev Apparaat, is een object of exemplaar $dev van het type Apparaat. Elk exemplaar van Apparaat kan verschillende waarden hebben in de eigenschappen.
Ondersteunde scenario's
- Definieer aangepaste typen in PowerShell met behulp van bekende objectgeoriënteerde programmeersemantiek zoals klassen, eigenschappen, methoden, overname, enzovoort.
- Fouten opsporen in typen 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 gemaakt 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 haakjes rond de klassenaam verplicht. De haakjes signaleren een typedefinitie voor PowerShell.
Voorbeeld van syntaxis en gebruik
In dit voorbeeld ziet u de minimale syntaxis die nodig is om een bruikbaar klasse te maken.
class Device {
[string]$Brand
}
$dev = [Device]::new()
$dev.Brand = "Microsoft"
$dev
Brand
-----
Microsoft
Klasse-eigenschappen
Eigenschappen zijn variabelen die zijn gedeclareerd op klassebereik. Een eigenschap kan van elk ingebouwd type of een instantie van een andere klasse zijn. 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 met behulp van de apparaatklasse definieert. De voorbeelden, na deze, laten zien hoe u apparaten toevoegt aan het rek en hoe u begint 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 bevatten. Methoden kunnen uitvoer retourneren. Gegevens die door een methode worden geretourneerd, kunnen elk gedefinieerd gegevenstype zijn.
Bij het definiëren van een methode voor een klasse verwijst u naar het huidige klasseobject met behulp van de $this automatische variabele. Hiermee hebt u toegang tot eigenschappen en andere methoden die zijn gedefinieerd in de huidige klasse.
Voorbeeld van een eenvoudige klasse met eigenschappen en methoden
De Rack-klasse uitbreiden om apparaten toe te voegen aan of 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
Methoden moeten een retourtype hebben gedefinieerd. Als een methode geen uitvoer retournt, moet het uitvoertype [void] zijn.
In klassemethoden worden geen objecten verzonden naar de pijplijn, behalve de objecten die worden vermeld in de return instructie . Er is geen onbedoelde uitvoer van de code naar de pijplijn.
Notitie
Dit verschilt in principe van de manier waarop PowerShell-functies de uitvoer verwerken, waarbij alles naar de pijplijn gaat.
Niet-beëindigingsfouten die vanuit een klassemethode naar de foutstroom worden geschreven, worden niet doorgegeven. U moet gebruiken throw om een beëindigingsfout aan het oppervlak te brengen.
Met behulp van de cmdlets kunt u nog steeds schrijven naar de uitvoerstromen van Write-* PowerShell vanuit een klassemethode. Dit moet echter worden vermeden, zodat met de methode alleen objecten worden gebruikt met behulp van de return -instructie.
Uitvoer van methode
In dit voorbeeld wordt geen onbedoelde uitvoer naar de pijplijn van klassemethoden gedemonstreerd, behalve op 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 u het exemplaar van de klasse maakt. 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 standaardparameterloze constructor. Deze constructor initialiseert alle leden naar hun standaardwaarden. Objecttypen en tekenreeksen krijgen null-waarden. Wanneer u een constructor definieert, wordt er geen standaardparameterloze constructor gemaakt. Maak een parameterloze constructor als dat nodig is.
Basissyntaxis van constructor
In dit voorbeeld wordt de klasse Device gedefinieerd met eigenschappen en een constructor. Als u deze klasse wilt gebruiken, moet de gebruiker waarden opgeven voor de parameters die in de constructor worden vermeld.
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 Device gedefinieerd met eigenschappen, een standaard constructor en een constructor om het exemplaar te initialiseren.
De standaard constructor stelt het merk in op Undefined en laat model en vendor-sku met null-waarden staan.
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 verbergt een eigenschap of methode. De eigenschap of methode is nog steeds toegankelijk voor de gebruiker en is beschikbaar in alle bereiken waarin het object beschikbaar is. Verborgen leden worden verborgen voor de cmdlet en kunnen niet worden weergegeven met tabinvulling of Get-Member IntelliSense buiten de klassedefinitie.
Zie voor meer informatie About_hidden.
Voorbeeld van het gebruik van 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.
Door het verborgen kenmerk te gebruiken, kan de ontwikkelaar het aantal sleuven verborgen houden en voorkomt u onbedoelde wijzigingen in de grootte van het rek.
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 de $r1 uitvoer. De grootte is echter gewijzigd door de constructor.
Statisch kenmerk
Het static kenmerk definieert een eigenschap of een methode die in de klasse bestaat en geen instantie nodig heeft.
Een statische eigenschap is altijd beschikbaar, onafhankelijk van klasse-instantiëring. Een statische eigenschap wordt gedeeld met alle exemplaren van de klasse. Er is altijd een statische methode beschikbaar. Alle statische eigenschappen zijn live voor de hele sessie.
Voorbeeld van het gebruik van statische kenmerken en methoden
Stel dat de racks die hier zijn gemaakt, aanwezig zijn in uw datacenter. U wilt dus de racks 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 telkens wanneer u dit voorbeeld gebruikt.
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 van het gebruik van 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 methoden en eigenschappen toevoegen of overschrijven, indien nodig.
PowerShell biedt geen ondersteuning voor meervoudige overname. Klassen kunnen niet van meer dan één klasse overnemen. U kunt echter wel interfaces voor dat doel gebruiken.
Overname-implementatie wordt gedefinieerd door de : operator. Dit betekent dat deze klasse wordt uitgebreid of dat deze interfaces worden geïmplementeerd. De afgeleide klasse moet altijd het meest links staan in de klassedeclaratie.
Voorbeeld van het gebruik van eenvoudige overnamesyntaxis
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 die na de basisklasse komt.
Class Derived : Base, Interface {...}
Voorbeeld van eenvoudige overname in PowerShell-klassen
In dit voorbeeld zijn de rack- en apparaatklassen die in de vorige voorbeelden worden gebruikt beter gedefinieerd om: herhalingen van eigenschappen voorkomen, algemene eigenschappen beter uitlijnen en algemene bedrijfslogica opnieuw gebruiken.
De meeste objecten in het datacenter zijn bedrijfsmiddelen, wat zinvol is om ze als assets bij te houden. Apparaattypen worden gedefinieerd door DeviceType de -enumeratie. Zie about_Enum voor meer informatie over -enumeraties.
In ons voorbeeld definiëren we alleen en Rack ComputeServer ; beide extensies voor de klasse 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
Basisklasse-constructors aanroepen
Als u een basisklasse constructor 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, declareer 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 overschrijven implementaties, cast u naar de basisklasse ([baseclass]$this) bij aanroepen.
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
Overnemen van interfaces
PowerShell-klassen kunnen een interface implementeren met dezelfde overnamesyntaxis die wordt gebruikt om basisklassen uit te breiden. Omdat interfaces meerdere overname toestaan, kan een PowerShell-klasse die een interface implementeert, overnemen van meerdere typen door de typenamen na de dubbele punt ( ) te scheiden met : komma's ( , ). Een PowerShell-klasse die een interface implementeert, moet alle leden van die interface implementeren. Het weglaten van de leden van de implementatie-interface veroorzaakt een parse-tijdfout in het script.
Notitie
PowerShell biedt momenteel geen ondersteuning voor het declareren van nieuwe interfaces in een PowerShell-script.
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 is geladen in de huidige sessie, mislukt using de instructie. Zie voor meer informatie over de using instructie about_Using.
Met using module de instructie worden klassen geïmporteerd uit de hoofdmodule ( ) van een ModuleToProcess scriptmodule of binaire module. Klassen die zijn gedefinieerd in geneste modules of klassen die zijn gedefinieerd in scripts die dot-source in de module zijn gedefinieerd, worden niet consistent geïmporteerd. Klassen die u beschikbaar wilt stellen aan gebruikers buiten de module, moeten worden gedefinieerd in de hoofdmodule.
Nieuw gewijzigde code laden tijdens de ontwikkeling
Tijdens het ontwikkelen van een scriptmodule is het gebruikelijk om wijzigingen aan te brengen in de code en vervolgens de nieuwe versie van de module te laden met behulp Import-Module van de parameter Force. Dit werkt alleen voor wijzigingen in functies in de hoofdmodule. Import-Module laadt geen geneste modules opnieuw. Er is ook geen manier om bijgewerkte klassen te laden.
Om ervoor te zorgen dat u de nieuwste versie gebruikt, moet u de module verwijderen met behulp van Remove-Module de cmdlet . Remove-Module verwijdert de hoofdmodule, alle geneste modules en eventuele klassen die in de modules zijn gedefinieerd. Vervolgens kunt u de module en de klassen opnieuw laden met en Import-Module de using module instructie .
Een andere veelvoorkomende ontwikkeling is het scheiden van uw code in verschillende bestanden. Als u een functie in het ene bestand hebt dat klassen gebruikt die zijn gedefinieerd in een andere module, moet u de instructie gebruiken om ervoor te zorgen dat de functies de benodigde using module klassedefinities hebben.
Het type PSReference wordt niet ondersteund door klasseleden
Het gebruik [ref] van de typecast met een klasselid mislukt op de stille manier. API's die gebruikmaken [ref] van parameters kunnen niet worden gebruikt met klasseleden. De PSReference-klasse is ontworpen ter ondersteuning van COM-objecten. COM-objecten hebben gevallen waarin u een waarde moet doorgeven via een verwijzing.
Zie [ref] PSReference Classvoor meer informatie over het type .