about_Classes

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. Dankzij de toevoeging van klassen kunnen ontwikkelaars en IT-professionals PowerShell gebruiken voor een breder scala aan use cases. 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 exemplaar van het apparaat kan verschillende waarden in de eigenschappen hebben.

Ondersteunde scenario's

  • Definieer aangepaste typen in PowerShell met behulp van vertrouwde objectgeoriënteerde programmeer semantiek, 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 haken 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 elk ingebouwd type of een exemplaar 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 gedefinieerd met behulp van de apparaatklasse . In de volgende voorbeelden ziet u 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 aannemen die invoergegevens leveren. Methoden kunnen uitvoer retourneren. Gegevens die door een methode worden geretourneerd, kunnen elk gedefinieerd gegevenstype zijn.

Wanneer u een methode voor een klasse definieert, 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 eenvoudige klasse met eigenschappen en methoden

Breid de Rack-klasse uit om apparaten toe te voegen en 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 retourneert, moet het uitvoertype zijn [void].

In klassemethoden worden er geen objecten verzonden naar de pijplijn, behalve objecten die in de return instructie worden vermeld. Er is geen onbedoelde uitvoer naar de pijplijn uit de code.

Notitie

Dit verschilt fundamenteel van de manier waarop PowerShell-functies uitvoer verwerken, waarbij alles naar de pijplijn gaat.

Niet-afsluitfouten die vanuit een klassemethode naar de foutstroom worden geschreven, worden niet doorgegeven. U moet een throw afsluitfout weergeven. Met behulp van de Write-* cmdlets kunt u nog steeds vanuit een klassemethode naar de uitvoerstromen van PowerShell schrijven. Dit moet echter worden vermeden, zodat met de methode objecten worden verzonden die alleen de return instructie gebruiken.

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 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.

De klasse kan nul of meer constructors hebben gedefinieerd. Als er geen constructor is gedefinieerd, krijgt de klasse een standaardconstructor zonder parameter. Met deze constructor initialiseert u alle leden naar hun standaardwaarden. Objecttypen en tekenreeksen krijgen null-waarden. Wanneer u constructor definieert, wordt er geen standaardconstructor zonder parameter gemaakt. Maak een parameterloze constructor als dat nodig is.

Basissyntaxis van constructor

In dit voorbeeld wordt de apparaatklasse 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 apparaatklasse gedefinieerd met eigenschappen, een standaardconstructor en een constructor om het exemplaar te initialiseren.

De standaardconstructor stelt het merk in op Niet-gedefinieerd en laat het model en de leverancier-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 zijn verborgen voor de Get-Member cmdlet en kunnen niet worden weergegeven met tabvoltooiing of IntelliSense buiten de klassedefinitie.

Zie About_hidden voor meer informatie.

Voorbeeld van het gebruik van verborgen kenmerken

Wanneer een Rack-object wordt gemaakt, is het aantal sleuven voor apparaten een vaste waarde die op elk moment niet 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 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 $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 heeft geen exemplaar nodig.

Een statische eigenschap is altijd beschikbaar, onafhankelijk van klasse-instantiëring. Een statische eigenschap wordt gedeeld in alle exemplaren van de klasse. Er is altijd een statische methode beschikbaar. Alle statische eigenschappen zijn live voor de hele sessieperiode.

Voorbeeld van het gebruik van statische kenmerken en methoden

Stel dat de racks 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))
        }
    }
}

Er bestaat een 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 uitvoert.

Eigenschappenvalidatiekenmerken

Met validatiekenmerken kunt u testen of waarden die aan eigenschappen zijn gegeven, voldoen aan gedefinieerde vereisten. Validatie wordt geactiveerd op het moment dat de waarde is 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 indien nodig methoden en eigenschappen toevoegen of overschrijven.

PowerShell biedt geen ondersteuning voor meerdere overnames. Klassen kunnen niet overnemen van meer dan één klasse. U kunt hiervoor echter interfaces gebruiken.

Overname-implementatie wordt gedefinieerd door de : operator. Dit betekent dat u deze klasse kunt uitbreiden of deze interfaces implementeert. De afgeleide klasse moet altijd het meest links worden gelaten in de klassedeclaratie.

Voorbeeld met behulp van eenvoudige overnamesyntaxis

In dit voorbeeld ziet u de eenvoudige syntaxis van de PowerShell-klasseovername.

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 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 bij te houden als assets. 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 van overschreven implementaties, castt 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. Als u de leden van de implementatieinterface weglaat, treedt er een parseringstijdfout op in het script.

Notitie

PowerShell biedt momenteel geen ondersteuning voor het declareren van nieuwe interfaces in 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 de using instructie. Zie about_Using voor meer informatie over de using instructie.

De using module instructie importeert klassen uit de hoofdmodule (ModuleToProcess) van een scriptmodule of binaire module. Het importeert niet consistent klassen die zijn gedefinieerd in geneste modules of klassen die zijn gedefinieerd in scripts die zijn gedefinieerd in dot-source in de module. Klassen die u beschikbaar wilt maken voor gebruikers buiten de module, moeten worden gedefinieerd in de hoofdmodule.

Nieuw gewijzigde code laden tijdens de ontwikkeling

Tijdens de ontwikkeling 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 van Import-Module 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 uitvoert, moet u de module verwijderen met behulp van de Remove-Module cmdlet. Remove-Module verwijdert de hoofdmodule, alle geneste modules en alle klassen die in de modules zijn gedefinieerd. Vervolgens kunt u de module en de klassen opnieuw laden met behulp van Import-Module en de using module instructie.

Een andere gangbare ontwikkelpraktijk is het scheiden van uw code in verschillende bestanden. Als u een functie hebt in een bestand dat klassen gebruikt die zijn gedefinieerd in een andere module, moet u de using module instructie gebruiken om ervoor te zorgen dat de functies de klassedefinities hebben die nodig zijn.

Het psReference-type wordt niet ondersteund door klasseleden

Het gebruik van de [ref] typecast met een klasselid op de achtergrond mislukt. API's die gebruikmaken van [ref] 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 per verwijzing moet doorgeven.

Zie PSReference Class voor meer informatie over het [ref] type.

Zie ook